Sharding-jdbc設置defaultDatasource無效問題解決&源碼分析思路

概要:sharding-jdbc中設置defaultDataSourcceName要配合setBindingTableGroups使用,否則默認數據源的配置無效

背景

在使用sharding-jdbc進行分庫分表的開發過程中,我們使用了3個數據源(庫):user0,user1,user2中,將主要需要拆分的用戶表及相關關係表拆分成了36張表分配在三個庫中,而除了這些需要分片的大表,還有一些量比較小的表,都存放在user0這個庫中。而由於使用了sharding-jdbc的分片數據源,無法單獨設置一個數據源,希望設置一個默認的路由規則,讓未配置具體分片的表都路由到user0這個庫。

這邊找到sharding-jdbc的官方文檔的配置手冊,其中有這麼一項配置:

因此,這邊設置:shardingRuleConfig.setDefaultDataSourceName(“user0”)

但是發現,最後在查詢未分片表時,沒有路由到user0上,而是路由到了user1。這邊就只能debug跟着源碼一層層調用,看看問題出在哪,爲什麼defaultDataSourcceName的設置沒有生效。

問題解決

在對sharding-jdbc源碼進行debug分析後,發現只配置defaultDataSourcceName是不會生效的,必須要配置bindingTableGroups,將所有需要按默認規則路由的表配置進去 ,這裏官方文檔有點坑,因爲並沒有說明這兩個配置的關聯性,而網上也沒有查到這類問題。這次解決了問題,也算熟悉了sharding-jdbc的源碼。

貼出我的數據源配置(通過java config配置的):

源碼分析過程

首先,我們通過setDefaultDataSourceName()這個方法開始,可以看到是直接修改了ShardingRuleConfiguration類的defaultDataSourceName屬性,通過idea的find usage功能搜索到這個屬性的使用者:

有兩個類使用了ShardingRuleConfiguration.defaultDataSourceName,其中的YamlShardingRuleConfiguration這個類只是對yml文件解析爲配置的一個類,因此從ShardingDataSourceNames.getDefaultDataSourceName看,:

這裏有三個調用者:

  • DefaultDatabaseRoutingEngine
  • ShardingRule
  • TableReferencesClauseParser

從類名來看,我們要找的就是DefaultDatabaseRoutingEngine。而ShardingRule類中主要是給TableBroadcastRoutingEngine和StandardRoutingEngine提供支持,這兩個類一個是用於全數據源查找,另一個是需要實現標準路由算法,不符合我們的需求。TableReferencesClauseParser是用於解析SQL的,並未在數據源路由中發揮作用。因此我們下面就是從DefaultDatabaseRoutingEngine着手:

這個類的功能很明顯,在route()方法中,將所有邏輯表路由至配置的默認數據源。可以看到logicTable是以對象屬性參與到路由邏輯中,因此,真正調用前需要通過構造器來創建這個路由引擎(RoutingEngine)。
我通過查找的引用,找到了創建RoutingEngine的位置:RoutingEngineFactory.newInstance()

從這個方法中可以看到,這裏是根據不同的判定條件,將SQL交由不同的RoutingEngine去執行數據源路由。而使用DefaultDatabaseRoutingEngine的條件則在shardingRule.isAllInDefaultDataSource()中,這個方法如下:

  • 對需查詢的邏輯表的判斷邏輯有三部分:
    1. 配置了路由規則返回false
    2. 配置了廣播表規則(查詢所有數據源)返回false
    3. 表名不爲空

這邊我們需要查詢的表沒有配置路由規則,只在user0中,因此前兩項都不滿足,所以只要表名(logicTables)不爲空,則可以使用DefaultDatabaseRoutingEngine路由至默認數據源。
但是通過Debug發現,我們這邊傳進來的的tableNames居然爲空:

很奇怪爲什麼我們的SQL沒有解析出來tableNames,這裏的tableNames()保存在sqlStatement中,而點進去能看到添加tableName的邏輯在TableFiller中:

而通過debug看到,這裏的sqlSegment是能拿到tableName的:

那麼應該就是fill判定爲false導致沒有向sqlStatement中添加tableName了,從上圖fill()方法中的判斷邏輯看,只有滿足shardingRule.contains()時,fill才爲true,而shardingRule.contains()的代碼爲:

  • 需要滿足三個條件其中之一:
    1. 配置了表分片規則
    2. 配置了綁定表
    3. 配置了廣播規則

因此,當我不想配置分片規則,又不想要在所有數據源中廣播查詢,並需要路由到指定的默認數據源時,必須要配置bindingTableRule,在官方文檔中找到設置:bindingTableGroups,也就是得到了上面最開始的結果:setBindingTableGroups()(跳至解決方案)

設置了綁定表後,發現的確在sqlStatement中拿到了tableName,並判定使用DefaultDatabaseRoutingEngine路由至設置的默認的數據源user0,達到了我們的目的。

這次雖然只是解決了一個小問題,但是基本上對sharding-jdbc中sql解析和路由的代碼流程有了初步的瞭解,下次在這裏面有什麼問題也能更快的定位問題,還是有一定的收穫的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章