Sharding-jdbc设置defaultDatasource无效问题解决和源码分析解决记录

目录

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)

Sharding-jdbc设置defaultDatasource无效问题解决和源码分析解决记录

解决办法:去掉配置内的spring.shardingsphere.sharding.tables.t_user.xxx,即可,如此未片表路由到默认数据源。

Sharding-jdbc设置defaultDatasource无效问题解决和源码分析解决记录

源码分析

首先,sharding-jdbc的入口是ShardingConnection#prepareStatement(),本质就是生成一个PreparedStatement对象,通过重写PreparedStatement来实现分片路由功能,这个具体就是ShardingPreparedStatement,因此debug断点入口在ShardingPreparedStatement,通过debug,最终确定到了RoutingEngineFactory.newInstance(ShardingRule, ShardingDataSourceMetaData, SQLStatement, OptimizeResult),路由引擎工厂RoutingEngineFactory根据不同的分片规则生成对应的路由引擎RoutingEngine

Sharding-jdbc设置defaultDatasource无效问题解决和源码分析解决记录

那么只要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

Sharding-jdbc设置defaultDatasource无效问题解决和源码分析解决记录

查看ShardingRule构造器调用链

Sharding-jdbc设置defaultDatasource无效问题解决和源码分析解决记录

发现ShardingRuleConfiguration是在应用启动时刻创建的,那么只能看sharding-jdbc的启动入口了,查看sharding-jdbc-spring-boot-starter内的spring.factories,查看自动装配类org.apache.shardingsphere.shardingjdbc.spring.boot.SpringBootConfiguration

Sharding-jdbc设置defaultDatasource无效问题解决和源码分析解决记录

关注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()

Sharding-jdbc设置defaultDatasource无效问题解决和源码分析解决记录

刚才分析完了创建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设置defaultDatasource无效问题解决和源码分析解决记录

Sharding-jdbc设置defaultDatasource无效问题解决和源码分析解决记录

总体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的同时还创建了ShardingRuleShardingContext两个核心类的对象。

ShardingContext持有两个属性ShardingRule、ShardingMetaData。

ShardingRule保存了表的分库分表配置,这些配置包括分库策略以及算法、分表策略以及算法,也就是说根据一个表以及这个表的列可以从ShardingRule中获取这个表的分库分表策略和算法。

ShardingMetaData则维护了数据源和表的元数据信息,其有两个属性:ShardingDataSourceMetaData和ShardingTableMetaData,分表表示数据源的元数据信息和表的元数据信息,这两个属性在ShardingMetaData的构造函数中被创建。

ShardingRuleConfiguration是分库分表配置的核心和入口,它可以包含多个TableRuleConfigurationMasterSlaveRuleConfiguration。每一组相同规则分片的表配置一个TableRuleConfiguration。一个TableRuleConfiguration表示一个表的分库分表策略配置,其持有两个类型为databaseShardingStrategyConfig和tableShardingStrategyConfig,分别表示分库策略配置和分表策略配置。

Sharding-JDBC会使用TableRuleConfiguration实例化TableRule对象。

一个TableRule对象表示一个逻辑表的库表资源,其维护一个类型为DataNode的集合属性actualDataNodes,这个DataNode集合表示此逻辑表对应的实际库表的集合,Sharding-JDBC做路由时即是根据此集合使用相应的算法进行实际的库表选取的。

类之间关系如下

Sharding-jdbc设置defaultDatasource无效问题解决和源码分析解决记录

核心就是ShardingRule,根据逻辑表获取TableRule,进而在执行路由使用,从TableRule获取对应的真实节点List<DataNode>

这个是sharding-jdbc的根本。

参考 https://www.jianshu.com/p/4cb5b2b68f8e

分库分表概念思维图

Sharding-jdbc设置defaultDatasource无效问题解决和源码分析解决记录

文件地址 https://gitee.com/yulewo123/mdpicture/blob/master/document/sharding-jdbc%E7%AC%94%E8%AE%B0.xmind

上一篇:面试官问我:看过sharding-jdbc的源码吗?我吧啦吧啦说了一通!!


下一篇:sharding-jdbc使用笔记