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