[zebra源码]如果定位到多个分库或分表怎么执行的?

假设分库分表情况如下

  • 分库 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线程池中并发执行  ,然后阻塞等待执行完毕,获取结果

完整目录:数据库中间件zebra源码分析

posted @ 2021-07-16 22:40  mushishi  阅读(195)  评论(0编辑  收藏  举报