本文主要基于 Sharding-JDBC 1.5.0 正式版
  • 1. 概述
  • 2. TableRule
    • 2.2.1 DataNode
    • 2.2.2 DynamicDataNode
    • 2.1 logicTable
    • 2.2 数据单元
    • 2.3 分库/分表策略
    • 2.4 主键生成
  • 3. ShardingRule
    • 3.1 dataSourceRule
    • 3.2 tableRules
    • 3.3 bindingTableRules
  • 4. ShardingStrategy
  • 5. ShardingAlgorithm
  • 666. 彩蛋

🙂🙂🙂关注微信公众号:【芋道源码】有福利:
  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言将得到认真回复。甚至不知道如何读源码也可以请教噢
  4. 新的源码解析文章实时收到通知。每周更新一篇左右
  5. 认真的源码交流微信群。

1. 概述

😆《SQL 解析》 已经告于段落,我们要开始新的旅程:《SQL 路由》。相比SQL解析,路由会容易理解很多,骗人是小🐷。整个系列预计会拆分成三小篇文章:
  1. 《分库分表配置》
  2. 《分表分库路由》
  3. 《Spring与YAML配置》
第一、二篇会在近期更新。第三篇会在《SQL 改写》、《SQL 执行》完成后进行更新。😈改写和执行相对有趣。
👼道友,您看,逗比博主“很有规划”,是关注公众号一波【芋道源码】还是分享朋友圈。

阅读本文之前,建议已经读过官方相关文章:
  • 《Sharding-JDBC 核心概念》
  • 《Sharding-JDBC 分表分库》
分表分库配置会涉及如下类:
  • 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。核心代码如下:
  1. // TableRuleBuilder.java
  2. publicstaticclassTableRuleBuilder{
  3. publicTableRule build(){
  4. KeyGenerator keyGenerator =null;
  5. if(null!= generateKeyColumn &&null!= keyGeneratorClass){
  6.           keyGenerator =KeyGeneratorFactory.createKeyGenerator(keyGeneratorClass);
  7. }
  8. returnnewTableRule(logicTable,dynamic, actualTables, dataSourceRule, dataSourceNames, databaseShardingStrategy, tableShardingStrategy, generateKeyColumn, keyGenerator);
  9. }
  10. }
  11. // TableRule.java
  12. publicTableRule(finalString logicTable,finalbooleandynamic,finalList<String> actualTables,finalDataSourceRule dataSourceRule,finalCollection<String> dataSourceNames,
  13. finalDatabaseShardingStrategy databaseShardingStrategy,finalTableShardingStrategy tableShardingStrategy,
  14. finalString generateKeyColumn,finalKeyGenerator keyGenerator){
  15. Preconditions.checkNotNull(logicTable);
  16. this.logicTable = logicTable;
  17. this.dynamic=dynamic;
  18. this.databaseShardingStrategy = databaseShardingStrategy;
  19. this.tableShardingStrategy = tableShardingStrategy;
  20. if(dynamic){// 动态表的分库分表数据单元
  21. Preconditions.checkNotNull(dataSourceRule);
  22. this.actualTables = generateDataNodes(dataSourceRule);
  23. }elseif(null== actualTables || actualTables.isEmpty()){// 静态表的分库分表数据单元
  24. Preconditions.checkNotNull(dataSourceRule);
  25. this.actualTables = generateDataNodes(Collections.singletonList(logicTable), dataSourceRule, dataSourceNames);
  26. }else{// 静态表的分库分表数据单元
  27. this.actualTables = generateDataNodes(actualTables, dataSourceRule, dataSourceNames);
  28. }
  29. this.generateKeyColumn = generateKeyColumn;
  30. this.keyGenerator = keyGenerator;
  31. }

2.2.1 DataNode

大多数业务场景下,我们使用静态分库分表数据单元,即 DataNode。如上文注释处 静态表的分库分表数据单元处所见,分成种判断,实质上第一种是将 logicTable 作为 actualTable,即在里不进行分表,是第二种的一种特例。
我们来看看 #generateDataNodes() 方法:
  1. // TableRule.java
  2. /**
  3. * 生成静态数据分片节点
  4. *
  5. * @param actualTables 真实表
  6. * @param dataSourceRule 数据源配置对象
  7. * @param actualDataSourceNames 数据源名集合
  8. * @return 静态数据分片节点
  9. */
  10. privateList<DataNode> generateDataNodes(finalList<String> actualTables,finalDataSourceRule dataSourceRule,finalCollection<String> actualDataSourceNames){
  11. Collection<String> dataSourceNames = getDataSourceNames(dataSourceRule, actualDataSourceNames);
  12. List<DataNode> result =newArrayList<>(actualTables.size()*(dataSourceNames.isEmpty()?1: dataSourceNames.size()));
  13. for(String actualTable : actualTables){
  14. if(DataNode.isValidDataNode(actualTable)){// 当 actualTable 为 ${dataSourceName}.${tableName} 时
  15.           result.add(newDataNode(actualTable));
  16. }else{
  17. for(String dataSourceName : dataSourceNames){
  18.               result.add(newDataNode(dataSourceName, actualTable));
  19. }
  20. }
  21. }
  22. return result;
  23. }
  24. /**
  25. * 根据 数据源配置对象 和 数据源名集合 获得 最终的数据源名集合
  26. *
  27. * @param dataSourceRule 数据源配置对象
  28. * @param actualDataSourceNames 数据源名集合
  29. * @return 最终的数据源名集合
  30. */
  31. privateCollection<String> getDataSourceNames(finalDataSourceRule dataSourceRule,finalCollection<String> actualDataSourceNames){
  32. if(null== dataSourceRule){
  33. returnCollections.emptyList();
  34. }
  35. if(null== actualDataSourceNames || actualDataSourceNames.isEmpty()){
  36. return dataSourceRule.getDataSourceNames();
  37. }
  38. return actualDataSourceNames;
  39. }
  • 第一种情况,自定义分布。 actualTable 为 ${dataSourceName}.${tableName} 时,即已经明确真实表所在数据源。
  1. TableRule.builder("t_order").actualTables(Arrays.asList("db0.t_order_0","db1.t_order_1","db1.t_order_2"))
  1. db0
  2. └── t_order_0
  3. db1
  4. ├── t_order_1
  5. └── t_order_2
  • 第二种情况,均匀分布
  1. TableRule.builder("t_order").actualTables(Arrays.asList("t_order_0","t_order_1"))
  1. db0
  2. ├── t_order_0
  3. └── t_order_1
  4. db1
  5. ├── t_order_0
  6. └── t_order_1
#getDataSourceNames() 使用 dataSourceRuleactualDataSourceNames 获取数据源的逻辑看起来有种“诡异”。实际 TableRuleBuilder 创建 TableRule 时,使用 dataSourceRule 而不要使用 actualDataSourceNames

2.2.2 DynamicDataNode

少数业务场景下,我们使用动态分库分表数据单元,即 DynamicDataNode。通过 dynamic=true 属性配置。生成代码如下:
  1. // TableRule.java
  2. privateList<DataNode> generateDataNodes(finalDataSourceRule dataSourceRule){
  3. Collection<String> dataSourceNames = dataSourceRule.getDataSourceNames();
  4. List<DataNode> result =newArrayList<>(dataSourceNames.size());
  5. for(String each : dataSourceNames){
  6.       result.add(newDynamicDataNode(each));
  7. }
  8. return result;
  9. }
😂 从代码上看,貌似和动态分库分表数据单元没一毛钱关系?!别捉鸡,答案在《分表分库路由》 上。

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 在未设置分库、分表算法的默认值。代码如下:
  1. // ShardingRule.java
  2. publicShardingRule(
  3. finalDataSourceRule dataSourceRule,finalCollection<TableRule> tableRules,finalCollection<BindingTableRule> bindingTableRules,
  4. finalDatabaseShardingStrategy databaseShardingStrategy,finalTableShardingStrategy tableShardingStrategy,finalKeyGenerator keyGenerator){
  5. // ... 省略部分代码
  6. this.databaseShardingStrategy =null== databaseShardingStrategy ?newDatabaseShardingStrategy(
  7. Collections.<String>emptyList(),newNoneDatabaseShardingAlgorithm()): databaseShardingStrategy;
  8. this.tableShardingStrategy =null== tableShardingStrategy ?newTableShardingStrategy(
  9. Collections.<String>emptyList(),newNoneTableShardingAlgorithm()): tableShardingStrategy;
  10. // ... 省略部分代码
  11. }
《分表分库路由》 会进一步说明。

666. 彩蛋

本文看似在水更,实是为《分表分库路由》做铺垫(一阵脸红😳)。
But,无论怎么说,道友,我做了新的关注二维码(感谢猫🐱先生),是不是可以推荐一波公众号给基佬。
恩,继续更新!
继续阅读
阅读原文