Sharding-jdbc设置defaultDatasource无效问题解决和源码分析解决记录
背景
在使用sharding-jdbc进行分库分表的开发过程中,我们使用了5个数据源(库):db_0,db_1,db_2,db_3,db_x中,将主要需要拆分的订单表和订单明细表分到db_0~db_3中,db_x作为默认库(不分的表存在此库),我希望未配置具体分片的表(比如t_user)都路由到默认库db_x,配置了spring.shardingsphere.sharding.default-data-source-name=dbx
,但是不生效,还是路由到了全库内,当然由于t_user在分库不存在而报错,难道是官方文档说错了? 还是自己配置错了
我的配置如下
#precise分库配置
# 定义数据源
spring.shardingsphere.datasource.names = db0,db1,db2,db3,dbx
# 配置数据源 db_0~db_3省略
# 配置数据源 db_x
spring.shardingsphere.datasource.dbx.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.dbx.driverClassName = com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.dbx.url=jdbc:mysql://localhost:3306/db_x?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
spring.shardingsphere.datasource.dbx.username=root
spring.shardingsphere.datasource.dbx.password=root
# 默认数据源,未分片的表默认执行库
spring.shardingsphere.sharding.default-data-source-name=dbx
# 这里是个sharding官方文档一个坑,只配置了default-data-source-name,对于不设置分片的表,会走全路由(比如插入t_user,会向每个库都插入),而我们的要求只是不分片的表路由到默认数据源
#spring.shardingsphere.sharding.binding-table-groups=t_user
#spring.shardingsphere.sharding.binding-tables=t_user
# 指定t_order表的主键生成策略为SNOWFLAKE
spring.shardingsphere.sharding.tables.t_order.key-generator.column=order_id
spring.shardingsphere.sharding.tables.t_order.key-generator.type=SNOWFLAKE
# 指定t_order_item表的主键生成策略为SNOWFLAKE
spring.shardingsphere.sharding.tables.t_order_item.key-generator.column=item_id
spring.shardingsphere.sharding.tables.t_order_item.key-generator.type=SNOWFLAKE
# 指定t_user表的主键生成策略为SNOWFLAKE #t_user不分表,只是配置主键生成策略
spring.shardingsphere.sharding.tables.t_user.key-generator.column=user_id
spring.shardingsphere.sharding.tables.t_user.key-generator.type=SNOWFLAKE
### 分库策略
# 分库分片健
spring.shardingsphere.sharding.tables.t_order.database-strategy.standard.sharding-column=order_id
# 分库分片算法
spring.shardingsphere.sharding.tables.t_order.database-strategy.standard.precise-algorithm-class-name=com.zyj.sharding.algorithm.DbPreciseShardingAlgorithm
# 分库分片健
spring.shardingsphere.sharding.tables.t_order_item.database-strategy.standard.sharding-column=order_id
# 分库分片算法
spring.shardingsphere.sharding.tables.t_order_item.database-strategy.standard.precise-algorithm-class-name=com.zyj.sharding.algorithm.DbPreciseShardingAlgorithm
出现问题,先网上搜索了下,默认数据源不生效,可以配置下spring.shardingsphere.sharding.binding-table-groups=t_user
,这样就可以,我这样尝试了也不行。没办法只能跟踪源码查看了
问题解决
在对sharding-jdbc源码进行debug分析后,发现配置defaultDataSourcceName,但是如果不分片的表配置了spring.shardingsphere.sharding.tables.未分片表
,那么会导致默认数据源失效。具体代码在org.apache.shardingsphere.core.route.router.sharding.RoutingEngineFactory#newInstance(ShardingRule, ShardingDataSourceMetaData, SQLStatement, OptimizeResult)
解决办法:去掉配置内的spring.shardingsphere.sharding.tables.t_user.xxx,即可,如此未片表路由到默认数据源。
源码分析
首先,sharding-jdbc的入口是ShardingConnection#prepareStatement()
,本质就是生成一个PreparedStatement对象,通过重写PreparedStatement来实现分片路由功能,这个具体就是ShardingPreparedStatement,因此debug断点入口在ShardingPreparedStatement,通过debug,最终确定到了RoutingEngineFactory.newInstance(ShardingRule, ShardingDataSourceMetaData, SQLStatement, OptimizeResult)
,路由引擎工厂RoutingEngineFactory根据不同的分片规则生成对应的路由引擎RoutingEngine
那么只要shardingRule.isAllInDefaultDataSource(tableNames)
结果为true即可,该方法代码如下:
//参数logicTableNames是本次操作的sql内涉及到的表集合,比如[t_user]
public boolean isAllInDefaultDataSource(final Collection<String> logicTableNames) {
for (String each : logicTableNames) {
if (findTableRule(each).isPresent() || isBroadcastTable(each)) {
return false;
}
}
return !logicTableNames.isEmpty();
}
//逻辑表包含在org.apache.shardingsphere.core.rule.ShardingRule.tableRules,则true,否则false
public Optional<TableRule> findTableRule(final String logicTableName) {
for (TableRule each : this.tableRules) {
if (each.getLogicTable().equalsIgnoreCase(logicTableName)) {
return Optional.of(each);
}
}
return Optional.absent();
}
isAllInDefaultDataSource总逻辑是:如果logicTableNames任一表是广播表或者包括在分片规则内,则不走默认数据源。那么关键就是this.tableRules,即ShardingRule.tableRules的来源了。属性this.tableRules是在ShardingRule构造器内赋值,它的来源是ShardingRuleConfiguration.tableRuleConfigs
查看ShardingRule构造器调用链
发现ShardingRuleConfiguration是在应用启动时刻创建的,那么只能看sharding-jdbc的启动入口了,查看sharding-jdbc-spring-boot-starter内的spring.factories,查看自动装配类org.apache.shardingsphere.shardingjdbc.spring.boot.SpringBootConfiguration
关注ShardingRuleConfigurationYamlSwapper.swap(YamlShardingRuleConfiguration)
方法,代码如下
public ShardingRuleConfiguration swap(final YamlShardingRuleConfiguration yamlConfiguration) {
ShardingRuleConfiguration result = new ShardingRuleConfiguration();//代码@1
for (Entry<String, YamlTableRuleConfiguration> entry : yamlConfiguration.getTables().entrySet()) {//代码@2
YamlTableRuleConfiguration tableRuleConfig = entry.getValue();
tableRuleConfig.setLogicTable(entry.getKey());
result.getTableRuleConfigs().add(tableRuleConfigurationYamlSwapper.swap(tableRuleConfig));
}
result.setDefaultDataSourceName(yamlConfiguration.getDefaultDataSourceName());//代码@3
result.getBindingTableGroups().addAll(yamlConfiguration.getBindingTables());//代码@4
result.getBroadcastTables().addAll(yamlConfiguration.getBroadcastTables());//代码@5
if (null != yamlConfiguration.getDefaultDatabaseStrategy()) {//代码@6
result.setDefaultDatabaseShardingStrategyConfig(shardingStrategyConfigurationYamlSwapper.swap(yamlConfiguration.getDefaultDatabaseStrategy()));
}
if (null != yamlConfiguration.getDefaultTableStrategy()) {//代码@7
result.setDefaultTableShardingStrategyConfig(shardingStrategyConfigurationYamlSwapper.swap(yamlConfiguration.getDefaultTableStrategy()));
}
if (null != yamlConfiguration.getDefaultKeyGenerator()) {//代码@8
result.setDefaultKeyGeneratorConfig(keyGeneratorConfigurationYamlSwapper.swap(yamlConfiguration.getDefaultKeyGenerator()));
}
Collection<MasterSlaveRuleConfiguration> masterSlaveRuleConfigs = new LinkedList<>();
for (Entry<String, YamlMasterSlaveRuleConfiguration> entry : yamlConfiguration.getMasterSlaveRules().entrySet()) {//代码@9
YamlMasterSlaveRuleConfiguration each = entry.getValue();
each.setName(entry.getKey());
masterSlaveRuleConfigs.add(masterSlaveRuleConfigurationYamlSwapper.swap(entry.getValue()));
}
result.setMasterSlaveRuleConfigs(masterSlaveRuleConfigs);
if (null != yamlConfiguration.getEncryptRule()) {//代码@10
result.setEncryptRuleConfig(encryptRuleConfigurationYamlSwapper.swap(yamlConfiguration.getEncryptRule()));
}
return result;
}
代码@1:创建ShardingRuleConfiguration,从下面代码可以看出,ShardingRuleConfiguration是整个sharding配置的载体
代码@2:配置,属性tableRuleConfigs,对应的配置是spring.shardingsphere.sharding.tables.xxxx
代码@3:配置,属性defaultDataSourceName,对应的配置是spring.shardingsphere.sharding.default-data-source-name
代码@4:配置,属性bindingTableGroups,对应的配置是spring.shardingsphere.sharding.binding-tables
代码@5:配置,属性broadcastTables,对应的配置是spring.shardingsphere.sharding.broadcast-tables
代码@6:配置默认分库策略,属性defaultDatabaseShardingStrategyConfig,对应的配置是spring.shardingsphere.sharding.default-database-strategy
代码@7:配置默认分表策略,属性defaultTableShardingStrategyConfig,对应的配置是spring.shardingsphere.sharding.default-table-strategy
代码@8:配置默认主键生成策略,属性defaultKeyGeneratorConfig,对应的配置是spring.shardingsphere.sharding.default-key-generator
代码@9:配置读写分离,属性masterSlaveRuleConfigs,无
代码@10:配置脱敏规则,属性encryptRuleConfig,无
从这个代码分析可以看出
ShardingRuleConfiguration 整个sharding配置的载体
TableRuleConfiguration 表示spring.shardingsphere.sharding.tables.xxx
回到org.apache.shardingsphere.core.rule.ShardingRule.tableRules的来源,来源于ShardingRuleConfiguration.tableRuleConfigs
,而tableRuleConfigs经过前面代码分析,对应配置spring.shardingsphere.sharding.tables.xxx,因此是我自己配错了,需要去除spring.shardingsphere.sharding.tables.t_user即可,网上说的配置bindingTableGroups不适用我当前使用的sharding-jdbc-spring-boot-starter-4.0.0-RC1版本。
至此该问题解决,既然分析了sharding-jdbc的启动,那么分析下完整启动过程,记录下
sharding-jdbc启动分析
接着上面源码分析
入口org.apache.shardingsphere.shardingjdbc.spring.boot.SpringBootConfiguration.dataSource()
刚才分析完了创建ShardingRuleConfiguration ,接着分析createDataSource创建数据源
public static DataSource createDataSource(
final Map<String, DataSource> dataSourceMap, final ShardingRuleConfiguration shardingRuleConfig, final Properties props) throws SQLException {
return new ShardingDataSource(dataSourceMap, new ShardingRule(shardingRuleConfig, dataSourceMap.keySet()), props);
}
参数dataSourceMap 对应spring.shardingsphere.datasource配置,key是数据源名称,value是数据源
参数shardingRuleConfig 对应整个sharding配置
参数props 对应spring.shardingsphere开头的配置
这里只是创建数据源ShardingDataSource,用于生成ShardingConnection,继而生成PreparedStatement(即ShardingPreparedStatement),ShardingDataSource持有所有的数据源以及分片策略,主键生成。
几个重要类
ShardingDataSource:数据源
ShardingContext:sharding 上下文,持有执行引擎,数据库类型,ShardingRule等一系列信息。在执行sql查询时候用到
ShardingConnection:数据库连接,用于生成PreparedStatement
ShardingPreparedStatement: 用户sql执行的入口
ShardingRuleConfiguration:sharding整个配置的载体,不包括spring.shardingsphere.datasource
配置
ShardingRule: 分片规则,持有所有分片规则和数据源名称,持有TableRule集合
TableRuleConfiguration: spring.shardingsphere.sharding.tables
配置的载体,每个配置对应一个该配置类
TableRule:表分片规则,分库规则,分表规则,持有TableRuleConfiguration
总体sharding-jdbc的启动还是蛮简单的
注意:org.apache.shardingsphere.shardingjdbc.spring.boot.SpringBootConfiguration.dataSourceMap属性是如何注入的呢?
该属性是spring.shardingsphere.datasource的载体,在配置类org.apache.shardingsphere.shardingjdbc.spring.boot.SpringBootConfiguration
被创建为bean时候,由于实现了EnvironmentAware,因此在setEnvironment内进行配置。
总结:
Sharding-JDBC的初始化主要包括两个方面:
1.数据源元数据信息和表元数据信息的收集。
2.表分库分表策略和算法的配置信息收集。
工厂类ShardingDataSourceFactory.createDataSource()方法在创建Sharding-JDBC的数据源实现类ShardingDataSource的同时还创建了ShardingRule、ShardingContext两个核心类的对象。
ShardingContext持有两个属性ShardingRule、ShardingMetaData。
ShardingRule保存了表的分库分表配置,这些配置包括分库策略以及算法、分表策略以及算法,也就是说根据一个表以及这个表的列可以从ShardingRule中获取这个表的分库分表策略和算法。
ShardingMetaData则维护了数据源和表的元数据信息,其有两个属性:ShardingDataSourceMetaData和ShardingTableMetaData,分表表示数据源的元数据信息和表的元数据信息,这两个属性在ShardingMetaData的构造函数中被创建。
ShardingRuleConfiguration是分库分表配置的核心和入口,它可以包含多个TableRuleConfiguration和MasterSlaveRuleConfiguration。每一组相同规则分片的表配置一个TableRuleConfiguration。一个TableRuleConfiguration表示一个表的分库分表策略配置,其持有两个类型为databaseShardingStrategyConfig和tableShardingStrategyConfig,分别表示分库策略配置和分表策略配置。
Sharding-JDBC会使用TableRuleConfiguration实例化TableRule对象。
一个TableRule对象表示一个逻辑表的库表资源,其维护一个类型为DataNode的集合属性actualDataNodes,这个DataNode集合表示此逻辑表对应的实际库表的集合,Sharding-JDBC做路由时即是根据此集合使用相应的算法进行实际的库表选取的。
类之间关系如下
核心就是ShardingRule,根据逻辑表获取TableRule,进而在执行路由使用,从TableRule获取对应的真实节点List<DataNode>
这个是sharding-jdbc的根本。
参考 https://www.jianshu.com/p/4cb5b2b68f8e
分库分表概念思维图
文件地址 https://gitee.com/yulewo123/mdpicture/blob/master/document/sharding-jdbc%E7%AC%94%E8%AE%B0.xmind