程序执行入口

ShardStatement


数据库中间件Zebra 分库分表源码分析01_sed


1.先重点看executeQuery

数据库中间件Zebra 分库分表源码分析01_初始化_02

​1.1 checkClosed,检查Statement对象是否关闭


1.2 根据filters 创建chain,然后获取下一个filter,执行filter的逻辑;


1.3 目前执行的filter只有catFilter, 只是对执行分片查询的过程,进行成功和异常的监控。


1.4 核心逻辑在executeQueryWithFilter


1.4.1 beforeQuery ,只是为了特殊处理 SELECT @@IDENTITY AS A,用来得到上一次插入记录时自动产生的id


就直接把generatedKey作为结果,加入到路由结果列表,也就是ShardResultSet ,然后返回结果,结束。


1.4.2 调用routingAndCheck进行路由

数据库中间件Zebra 分库分表源码分析01_sql_03


1.4.3 调用DefaultShardRouter 的 router 进行路由 :


(1) 先从本地缓存中查询sql的解析结果result缓存,如果没有查询到,就开始调用parseInternal解析sql


(2) 根据sql来初始化对应的MysqlLexer(词法分析器),并且继续初始化SQLStatementParser,底层调用druid的SQLStatementParser


的parseStatementList方法,对语句进行解析,根据token类型解析出不同类型的statement,最后根据解析语句的条数,包装成对应的sqlParsedResult或者multiSQLParsedResult;parseZebraHint 解析出SQLHint


(3) 转化后的结果需要存入解析结果的本地缓存parseSqlCache


(4) 从返回的parsedResult,获取出sqlHint,来设置并发级别


(5) 如果是多条查询语句 multi queries , 就调用multiQueriesRouter;循环刚才解析的sqlParsedResults,获取shardRules。


(6) 根据每一个sqlParsedResult的路由上下文routerContext,获取对应的tableSet,需要和配置文件中解析出的tableShardRules进 行匹配,最后校验路由规则只能为1条,否则直接抛出异常。


(7) 对解析出的1条rule,调用routerOneRule,解析出1条路由结果routerResult : 调用tableShardResult.eval方法


(8) 从上下文ctx获取sqlType,然后获取hintShardColumn,然后根据分片列获取对应维度的路由规则rule,调用findDimensionRule


判断hintShardColumn和配置文件中的分表维度是否匹配,来返回命中的rule。


(9) 如果获取的DimensionRule不为空,就调用evalDimension解析路由结果,解析某个分片维度的路由规则


数据库中间件Zebra 分库分表源码分析01_初始化_04


(10) 从解析的结果中获取sqlHint,然后获取是否是isBatchInsert,继续调用ShardColumnValueUtil.eval解析

数据库中间件Zebra 分库分表源码分析01_sed_05


(11) 循环分片规则里面的分片列,根据每一个分片列shardColumn,获取分片参数列表datas


(12) 先从threadLocal,也就是本地缓存map中获取分片列参数,然后放到tmpResult 暂存结果。


(13) 如果不是从threadLocal(线程本地变量),那么从sql或者params获取分片列shard column参数: 根据sql不同的表达式类型解 析,解析后的结果放到List<Pair> pairs;再循环pairs,根据不同的sql操作符类型解析出value(SQLValuableExpr, SQLVariantRefExpr,SQLInListExpr),最后返回result


(15) 如果sql中有 = ,>, < ,就进一步解析出range参数,然后把解析出的columnValues 存入到ShardEvalContext上下文ctx


(16) 最后根据sql类型判断


数据库中间件Zebra 分库分表源码分析01_sql_06


(17) 如果是SELECT, 就调用rule.eval(ctx),进行路由解析,实现是DefaultDimensionRule


解析白名单的,解析后的db和table存入到result;


(18) 并且进一步解析出dbIndex,也存入到result;检查update或者delete操作,没有shard key是不允许的;最后清除本地线程变量


设置合并上下文 mergeContext


(19) 判断是否为批量插入,如果是,调用buildBatchInsertSqls,从shardResult获取db和tables;然后循环每个库,每个表,进行sql重写。


(19) 开始根据路由结果的的db和table,构建sql,调用buildSqls方法


(20) 循环路由结果的db和table的集合,然后获取对应的值,就是physicalTable,然后调用sqlRewrite.rewrite进行sql重写。


数据库中间件Zebra 分库分表源码分析01_sql_07