🙂🙂🙂關注微信公眾號:【芋道源碼】有福利:
- RocketMQ / MyCAT / Sharding-JDBC 所有源碼分析文章列表
- RocketMQ / MyCAT / Sharding-JDBC 中文注釋源碼 GitHub 地址
- 您對於源碼的疑問每條留言都將得到認真回復。甚至不知道如何讀源碼也可以請教噢。
- 新的源碼解析文章實時收到通知。每周更新一篇左右。
- 認真的源碼交流微信群。
本文主要基於 Sharding-JDBC 1.5.0 正式版
1. 概述
😆《SQL 解析》 已經告於段落,我們要開始新的旅程:《SQL 路由》。相比SQL解析,路由會容易理解很多,騙人是小🐷。整個系列預計會拆分成三小篇文章:
- 《分庫分表配置》
- 《分表分庫路由》
- 《Spring與YAML配置》
第一、二篇會在近期更新。第三篇會在《SQL 改寫》、《SQL 執行》完成后進行更新。😈改寫和執行相對有趣。
👼道友,您看,逗比博主“很有規划”,是關注公眾號一波【芋道源碼】還是分享朋友圈。
閱讀本文之前,建議已經讀過官方相關文章:
分表分庫配置會涉及如下類:
- TableRule 表規則配置對象
- ShardingRule 分庫分表規則配置對象
- ShardingStrategy 分片策略
- ShardingAlgorithm 分片算法
我們來一起逐個類往下看。
Sharding-JDBC 正在收集使用公司名單:傳送門。
🙂 你的登記,會讓更多人參與和使用 Sharding-JDBC。傳送門
Sharding-JDBC 也會因此,能夠覆蓋更多的業務場景。傳送門
登記吧,騷年!傳送門
2. TableRule
TableRule,表規則配置對象,內嵌 TableRuleBuilder 對象進行創建。
2.1 logicTable
數據分片的邏輯表,對於水平拆分的數據庫(表),同一類表的總稱。
例:訂單數據根據主鍵尾數拆分為10張表,分別是t_order_0到t_order_9,他們的邏輯表名為t_order。
2.2 數據單元
Sharding-JDBC 有兩種類型數據單元:
- DataNode :靜態分庫分表數據單元
數據分片的最小單元,由數據源名稱和數據表組成。
例:ds_1.t_order_0。配置時默認各個分片數據庫的表結構均相同,直接配置邏輯表和真實表對應關系即可。
如果各數據庫的表結果不同,可使用ds.actual_table配置。
- DynamicDataNode :動態表的分庫分表數據單元
邏輯表和真實表不一定需要在配置規則中靜態配置。
比如按照日期分片的場景,真實表的名稱隨着時間的推移會產生變化。
此類需求Sharding-JDBC是支持的,不過目前配置並不友好,會在新版本中提升。
TableRuleBuilder 調用 #build()
方法創建 TableRule。核心代碼如下:
// TableRuleBuilder.java
public static class TableRuleBuilder {
public TableRule build() {
KeyGenerator keyGenerator = null;
if (null != generateKeyColumn && null != keyGeneratorClass) {
keyGenerator = KeyGeneratorFactory.createKeyGenerator(keyGeneratorClass);
}
return new TableRule(logicTable, dynamic, actualTables, dataSourceRule, dataSourceNames, databaseShardingStrategy, tableShardingStrategy, generateKeyColumn, keyGenerator);
}
}
// TableRule.java
public TableRule(final String logicTable, final boolean dynamic, final List<String> actualTables, final DataSourceRule dataSourceRule, final Collection<String> dataSourceNames,
final DatabaseShardingStrategy databaseShardingStrategy, final TableShardingStrategy tableShardingStrategy,
final String generateKeyColumn, final KeyGenerator keyGenerator) {
Preconditions.checkNotNull(logicTable);
this.logicTable = logicTable;
this.dynamic = dynamic;
this.databaseShardingStrategy = databaseShardingStrategy;
this.tableShardingStrategy = tableShardingStrategy;
if (dynamic) { // 動態表的分庫分表數據單元
Preconditions.checkNotNull(dataSourceRule);
this.actualTables = generateDataNodes(dataSourceRule);
} else if (null == actualTables || actualTables.isEmpty()) { // 靜態表的分庫分表數據單元
Preconditions.checkNotNull(dataSourceRule);
this.actualTables = generateDataNodes(Collections.singletonList(logicTable), dataSourceRule, dataSourceNames);
} else { // 靜態表的分庫分表數據單元
this.actualTables = generateDataNodes(actualTables, dataSourceRule, dataSourceNames);
}
this.generateKeyColumn = generateKeyColumn;
this.keyGenerator = keyGenerator;
}
2.2.1 DataNode
大多數業務場景下,我們使用靜態分庫分表數據單元,即 DataNode。如上文注釋處 靜態表的分庫分表數據單元
處所見,分成兩種判斷,實質上第一種是將 logicTable
作為 actualTable
,即在庫里不進行分表,是第二種的一種特例。
我們來看看 #generateDataNodes()
方法:
// TableRule.java
/**
* 生成靜態數據分片節點
*
* @param actualTables 真實表
* @param dataSourceRule 數據源配置對象
* @param actualDataSourceNames 數據源名集合
* @return 靜態數據分片節點
*/
private List<DataNode> generateDataNodes(final List<String> actualTables, final DataSourceRule dataSourceRule, final Collection<String> actualDataSourceNames) {
Collection<String> dataSourceNames = getDataSourceNames(dataSourceRule, actualDataSourceNames);
List<DataNode> result = new ArrayList<>(actualTables.size() * (dataSourceNames.isEmpty() ? 1 : dataSourceNames.size()));
for (String actualTable : actualTables) {
if (DataNode.isValidDataNode(actualTable)) { // 當 actualTable 為 ${dataSourceName}.${tableName} 時
result.add(new DataNode(actualTable));
} else {
for (String dataSourceName : dataSourceNames) {
result.add(new DataNode(dataSourceName, actualTable));
}
}
}
return result;
}
/**
* 根據 數據源配置對象 和 數據源名集合 獲得 最終的數據源名集合
*
* @param dataSourceRule 數據源配置對象
* @param actualDataSourceNames 數據源名集合
* @return 最終的數據源名集合
*/
private Collection<String> getDataSourceNames(final DataSourceRule dataSourceRule, final Collection<String> actualDataSourceNames) {
if (null == dataSourceRule) {
return Collections.emptyList();
}
if (null == actualDataSourceNames || actualDataSourceNames.isEmpty()) {
return dataSourceRule.getDataSourceNames();
}
return actualDataSourceNames;
}
- 第一種情況,自定義分布。
actualTable
為${dataSourceName}.${tableName}
時,即已經明確真實表所在數據源。
TableRule.builder("t_order").actualTables(Arrays.asList("db0.t_order_0", "db1.t_order_1", "db1.t_order_2"))
db0
└── t_order_0
db1
├── t_order_1
└── t_order_2
- 第二種情況,均勻分布。
TableRule.builder("t_order").actualTables(Arrays.asList("t_order_0", "t_order_1"))
db0
├── t_order_0
└── t_order_1
db1
├── t_order_0
└── t_order_1
#getDataSourceNames()
使用 dataSourceRule
和 actualDataSourceNames
獲取數據源的邏輯看起來有種“詭異”。實際 TableRuleBuilder 創建 TableRule 時,使用 dataSourceRule
而不要使用 actualDataSourceNames
。
2.2.2 DynamicDataNode
少數業務場景下,我們使用動態分庫分表數據單元,即 DynamicDataNode。
通過 dynamic=true
屬性配置。生成代碼如下:
// TableRule.java
private List<DataNode> generateDataNodes(final DataSourceRule dataSourceRule) {
Collection<String> dataSourceNames = dataSourceRule.getDataSourceNames();
List<DataNode> result = new ArrayList<>(dataSourceNames.size());
for (String each : dataSourceNames) {
result.add(new DynamicDataNode(each));
}
return result;
}
😂 從代碼上看,貌似和動態分庫分表數據單元沒一毛錢關系?!別捉雞,答案在《分表分庫路由》 上。
2.3 分庫/分表策略
databaseShardingStrategy
:分庫策略tableShardingStrategy
:分表策略
當分庫/分表策略不配置時,使用 ShardingRule 配置的分庫/分表策略。
2.4 主鍵生成
generateKeyColumn
:主鍵字段keyGenerator
:主鍵生成器
當主鍵生成器不配置時,使用 ShardingRule 配置的主鍵生成器。
3. ShardingRule
ShardingRule,分庫分表規則配置對象,內嵌 ShardingRuleBuilder 對象進行創建。
其中 databaseShardingStrategy、tableShardingStrategy、keyGenerator、defaultGenerator 和 TableRule 屬性重復,用於當 TableRule 未配置對應屬性,使用 ShardingRule 提供的該屬性。
3.1 dataSourceRule
dataSourceRule
,數據源配置對象。ShardingRule 需要數據源配置正確。這點和 TableRule 是不同的。TableRule 對 dataSourceRule
只使用數據源名字,最終執行SQL 使用數據源名字從 ShardingRule 獲取數據源連接。大家可以回到本文【2.2.1 DataNode】細看下 DataNode 的生成過程。
3.2 tableRules
tableRules
,表規則配置對象集合。
3.3 bindingTableRules
指在任何場景下分片規則均一致的主表和子表。
例:訂單表和訂單項表,均按照訂單ID分片,則此兩張表互為BindingTable關系。
BindingTable關系的多表關聯查詢不會出現笛卡爾積關聯,關聯查詢效率將大大提升。
😈 這么說,可能不太容易理解。《分表分庫路由》,我們在源碼的基礎上,好好理解下。非常重要,特別是性能優化上面。
4. ShardingStrategy
ShardingStrategy,分片策略。
- 針對分庫、分表有兩個子類。
- DatabaseShardingStrategy,使用分庫算法進行分片
- TableShardingStrategy,使用分表算法進行分片
《分表分庫路由》 會進一步說明。
5. ShardingAlgorithm
ShardingAlgorithm,分片算法。
- 針對分庫、分表有兩個子接口。
- 針對分片鍵數量分成:無分片鍵算法、單片鍵算法、多片鍵算法。
其中 NoneKeyDatabaseShardingAlgorithm、NoneTableShardingAlgorithm 為 ShardingRule 在未設置分庫、分表算法的默認值。代碼如下:
// ShardingRule.java
public ShardingRule(
final DataSourceRule dataSourceRule, final Collection<TableRule> tableRules, final Collection<BindingTableRule> bindingTableRules,
final DatabaseShardingStrategy databaseShardingStrategy, final TableShardingStrategy tableShardingStrategy, final KeyGenerator keyGenerator) {
// ... 省略部分代碼
this.databaseShardingStrategy = null == databaseShardingStrategy ? new DatabaseShardingStrategy(
Collections.<String>emptyList(), new NoneDatabaseShardingAlgorithm()) : databaseShardingStrategy;
this.tableShardingStrategy = null == tableShardingStrategy ? new TableShardingStrategy(
Collections.<String>emptyList(), new NoneTableShardingAlgorithm()) : tableShardingStrategy;
// ... 省略部分代碼
}
《分表分庫路由》 會進一步說明。
666. 彩蛋
本文看似在水更,實是為《分表分庫路由》做鋪墊(一陣臉紅😳)。
But,無論怎么說,道友,我做了新的關注二維碼(感謝貓🐱先生),是不是可以推薦一波公眾號給基佬。
恩,繼續更新。