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)

image-20210323222559402

解決辦法:去掉配置內的spring.shardingsphere.sharding.tables.t_user.xxx,即可,如此未片表路由到默認數據源。

image-20210323223220154

源碼分析

首先,sharding-jdbc的入口是ShardingConnection#prepareStatement(),本質就是生成一個PreparedStatement對象,通過重寫PreparedStatement來實現分片路由功能,這個具體就是ShardingPreparedStatement,因此debug斷點入口在ShardingPreparedStatement,通過debug,最終確定到了RoutingEngineFactory.newInstance(ShardingRule, ShardingDataSourceMetaData, SQLStatement, OptimizeResult),路由引擎工廠RoutingEngineFactory根據不同的分片規則生成對應的路由引擎RoutingEngine

image-20210323224333315

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

image-20210323224947217

查看ShardingRule構造器調用鏈

image-20210323230550120

發現ShardingRuleConfiguration是在應用啟動時刻創建的,那么只能看sharding-jdbc的啟動入口了,查看sharding-jdbc-spring-boot-starter內的spring.factories,查看自動裝配類org.apache.shardingsphere.shardingjdbc.spring.boot.SpringBootConfiguration

20210324000242

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

image-20210323235347006

剛才分析完了創建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

image-20210324003330044

image-20210324003337856

總體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啟動

核心就是ShardingRule,根據邏輯表獲取TableRule,進而在執行路由使用,從TableRule獲取對應的真實節點List<DataNode>

這個是sharding-jdbc的根本。

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

分庫分表概念思維圖

sharding-jdbc

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM