為什么要分庫分表
在大型網(wǎng)站中,當(dāng)用戶量以及用戶產(chǎn)生的業(yè)務(wù)數(shù)據(jù)量達(dá)到單庫單表性能極限時,為了支撐業(yè)務(wù)可持續(xù)發(fā)展,對于重要的核心業(yè)務(wù)必然要進(jìn)行分庫分表來存儲業(yè)務(wù)數(shù)據(jù)。
對于非核心業(yè)務(wù)產(chǎn)生的大量數(shù)據(jù),例如爬蟲爬取的信息,論壇產(chǎn)生的數(shù)據(jù)等,可以考慮把數(shù)據(jù)保存在像MongoDB這樣的NoSQL存儲里面,這些存儲會自動分片存儲業(yè)務(wù)數(shù)據(jù),不需要業(yè)務(wù)邏輯進(jìn)行干預(yù)。
分庫分表的缺點
分庫分表之后實際會產(chǎn)生一系列問題,對于每種問題我們需要針對性進(jìn)行解決,保障系統(tǒng)穩(wěn)定運行。
下面就分庫分表會產(chǎn)生的問題進(jìn)行列舉:
- 數(shù)據(jù)的獲取需要通過改寫路由去多個分片獲取數(shù)據(jù),然后進(jìn)行聚合。對于某些SQL需要全庫表路由來獲取數(shù)據(jù),有的SQL甚至需要把SQL語句改寫成笛卡爾積去獲取分片數(shù)據(jù),這中間會產(chǎn)生大量數(shù)據(jù)庫連接造成數(shù)據(jù)庫資源浪費。
- 分庫分表之后需要分布式事務(wù)來保證數(shù)據(jù)一致性。對于不同業(yè)務(wù)可以采用不同的分布式事務(wù)實現(xiàn)方式來對數(shù)據(jù)一致性提供不同的保障級別,這其實是一種業(yè)務(wù)的權(quán)衡。
- 對于分頁數(shù)據(jù)的獲取,最好是采用異構(gòu)數(shù)據(jù)到ElasticSearch或者其他分布式存儲中,既能提供高性能的檢索,也能避免由于分庫分表導(dǎo)致分頁數(shù)據(jù)獲取困難的問題。當(dāng)然是用匯總表對業(yè)務(wù)數(shù)據(jù)匯總,提供一個兜底方案也是很有必要的。
什么是分庫分表
分庫分表就是把用戶數(shù)據(jù)進(jìn)行拆分,存儲到不同的數(shù)據(jù)庫的不同數(shù)據(jù)表中。
分庫分表主要是用來承載用戶的寫負(fù)載,用戶數(shù)據(jù)在單庫單表存儲量太大,會導(dǎo)致用戶讀取阻塞,進(jìn)而導(dǎo)致用戶無法寫入。
對于讀負(fù)載可以通過讀寫分離,異構(gòu)到ElasticSearch等方案進(jìn)行很好的解決。
對于寫負(fù)載只能通過分庫分表的方案進(jìn)行解決。
怎么進(jìn)行分庫分表
分庫分庫的第一個要點是如何拆分?jǐn)?shù)據(jù)。
大體有以下幾種拆分方案,每種方案都有優(yōu)點也有缺點:
- 按時間段進(jìn)行拆分
- 取模法
- 查表法
一般業(yè)務(wù)中使用的都是取模法,因為通過取模法,業(yè)務(wù)數(shù)據(jù)可以均勻分布在數(shù)據(jù)庫中,用戶請求也可以均勻的請求不同的數(shù)據(jù)庫,可以避免熱點表,但是對于不同用戶的數(shù)據(jù)的交互或者聯(lián)合查詢就需要進(jìn)行多次查詢。
當(dāng)然在某些業(yè)務(wù)場景下使用其他拆分方案可能會更好,例如歷史日志,可以采用日志數(shù)據(jù)按照時間拆分,進(jìn)行歸檔的方式來進(jìn)行分庫分表。
分庫分表的第二個要點就是拆分之后SQL如何路由,如何執(zhí)行。
對于這一點一般采用現(xiàn)有的框架來進(jìn)行實現(xiàn),如MyCAT,Sharding-Jdbc等,他們基于不同的模式為我們封裝好了拆分之后SQL路由和執(zhí)行的細(xì)節(jié)。
其中MyCAT是使用代理服務(wù)器的方式實現(xiàn),Sharding-Jdbc是使用嵌入應(yīng)用系統(tǒng)中直接調(diào)用數(shù)據(jù)庫的方式實現(xiàn)的。
實踐中可以根據(jù)不同項目組的情況使用不同的方案。
分庫分表實踐
下面介紹通過Sharding-Jdbc來實現(xiàn)分庫分表。通過Sharding-Jdbc可以很方便地實現(xiàn)數(shù)據(jù)庫的拆分,SQL自動改寫,路由,請求結(jié)果歸并等具體的數(shù)據(jù)處理細(xì)節(jié)。
下面介紹一個簡單的分庫分表的實現(xiàn)流程,更多實現(xiàn)細(xì)節(jié)和使用方案可以參考官方文檔。
- 引入Jar包
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-core</artifactId>
<version>4.1.1</version>
</dependency>
- 配置數(shù)據(jù)源
Map<String, DataSource> dataSourceMap = new HashMap<>();
*// 配置第 1 個數(shù)據(jù)源*
DruidDataSource dataSource1 = new DruidDataSource();
dataSource1.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource1.setUrl("jdbc:mysql://localhost:3306/continuous?useUnicode=true&characterEncoding=utf-8&useSSL=false");
dataSource1.setUsername("root");
dataSource1.setPassword("12345678");
dataSourceMap.put("ds0", dataSource1);
*// 配置第 2 個數(shù)據(jù)源*
DruidDataSource dataSource2 = new DruidDataSource();
dataSource2.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource2.setUrl("jdbc:mysql://localhost:3306/continuous2?useUnicode=true&characterEncoding=utf-8&useSSL=false");
dataSource2.setUsername("root");
dataSource2.setPassword("12345678");
dataSourceMap.put("ds1", dataSource2);
- 配置分庫分表的方案
*// 配置 t_order 表規(guī)則*
TableRuleConfiguration orderTableRuleConfig =
new TableRuleConfiguration("t_order", "ds0.order0, ds1.order1");
*// 配置分庫策略*
orderTableRuleConfig.setDatabaseShardingStrategyConfig(
new StandardShardingStrategyConfiguration("user_id", databaseShardingAlgorithm));
*// 配置分表策略*
orderTableRuleConfig.setTableShardingStrategyConfig(
new StandardShardingStrategyConfiguration("user_id", tableShardingAlgorithm));
*// 配置業(yè)務(wù)主鍵ID生成方案*
orderTableRuleConfig.setKeyGeneratorConfig(
new KeyGeneratorConfiguration("SNOWFLAKE", "user_id"));
*// 配置分片規(guī)則*
ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration();
shardingRuleConfig.getTableRuleConfigs().add(orderTableRuleConfig);
*// 設(shè)置屬性*
Properties properties = new Properties();
properties.put("sql.show", true);
*// 創(chuàng)建 ShardingSphereDataSource*
return ShardingDataSourceFactory.*createDataSource*(dataSourceMap, shardingRuleConfig, properties);
- 基于業(yè)務(wù)進(jìn)行不同的定制
對于不同業(yè)務(wù)的查詢,有時需要使用Hint的方式進(jìn)行分庫分表。
最佳實踐和好的資料
文章中說明什么情況下分庫,什么情況下分表。
數(shù)據(jù)量大,就分表;并發(fā)高,就分庫。
還詳細(xì)比較了三種數(shù)據(jù)庫拆分方案的細(xì)節(jié)。
- Hit分片方式的使用,有幾篇文章寫很不錯
- 在這篇文章有使用查表法對于非分片key查詢數(shù)據(jù)的支持,很值得借鑒。