假設分庫分表情況如下
- 分庫 id0:分表 test_0 、 test_1
- 分庫 id1: 分表 test_2、 test_3
sql語句: select test.* from test
一、路由結果
DefaultRouter#router
路由出來的結果兩個 RouterResult, 每個里邊有多個分表的sql, 即所有分庫下的所有test分表
dbName: id0
sqls:
- SELECT test_0.* \nFROM test_0
- SELECT test_1.* \nFROM test_1
dbName: id1
sqls:
- SELECT test_2.* \nFROM test_2
- SELECT test_3.* \nFROM test_3
二、執行過程
如果路由定位到多個分庫,會根據并發度n,將每個分庫的sql語句 拆分成n個任務,放入線程池執行
默認單庫并發度 concurrentLevel = 1, 執行過程為
(1) 執行分庫id0里各分表的sql
- 從分庫id0 對應的數據源中一個數據連接,創建 (SELECT test_0.* \nFROM test_0)的 statement
- 從分庫id0 對應的數據源中一個數據連接,創建l (SELECT test_1.* \nFROM test_1 )的statement
- 把這兩個statement包裹到一個線程task中
(2) 執行分庫id1里各分表的sql
- 從分庫id1 對應的數據源中一個數據連接,創建 (SELECT test_2.* \nFROM test_2 ) 的statement
- 從分庫id1 對應的數據源中一個數據連接,創建l (SELECT test_3.* \nFROM test_3 )的statement
- 把這兩個statement包裹到一個線程task中
(3) 把這兩個task 丟到 SQLThreadPoolExecutor 中執行, 阻塞等待執行完畢
三、結果集合并
ShardResultSet#init() -> ShardResultSetMerger.merge 合并這四個 ResultSet
ShardResultSet 內部包含多個sql執行的結果集 ResultSet, 它實現了 ResultSet ,當從它遍歷查詢結果的時候,會根據 MergeContext( join、limit…etc)來組合結果數據
debug單測入口:
com.dianping.zebra.shard.jdbc.MultiDBPreparedStatementLifeCycleTest#testSingleRouterResult1
總結
路由定位到多個分庫或分表的執行邏輯:
ShardPrepardStatement#normalSelectExecute
會依次執行這多個路由目標分庫 RouterResult 內的語句,然后 ShardPrepardStatement#executeQueryByOriginal
執行單個分庫內的所有sql, 如果它發現有多個sql需要執行,則會根據 單庫并發度的配置 concurrentLevel=1
(1) 默認concurrentLevel = 1 ,每個分庫內不同表的sql, 先創建對應的 statement, 然后會打包到一個task里
(2) 如果 concurrentLevel > 1, 則每個分庫會獲取 concurrentLevel 個數據庫連接,將這幾條分表的sql均攤到這幾個數據庫連接,創建多個statement, 包成 concurrentLevel 個線程task
(3) 然后丟到java線程池中并發執行 ,然后阻塞等待執行完畢,獲取結果