程序执行入口
ShardStatement
1.先重点看executeQuery
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进行路由
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解析路由结果,解析某个分片维度的路由规则
(10) 从解析的结果中获取sqlHint,然后获取是否是isBatchInsert,继续调用ShardColumnValueUtil.eval解析
(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类型判断
(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重写。