ShardingJDBC查询的执行过程

跟着调式看代码的笔记,其中我觉得不重要的部分就直接用符号略过了,关于符号可参见:前略( https://saaavsaaa.github.io/aaa/SpringBoot_ShardingJdbc_Code_Load_Run_Insert.html)

我是mybatis+shardingjdbc+druid

select语句(简单版)

org/apache/ibatis/binding/MapperMethod.execute [case SELECT]

因为是简单版,所以依然是CachingExecutor == SimpleExecutor

org/apache/ibatis/executor/BaseExecutor.query -> queryFromDatabase -> doQuery –> org/apache/ibatis/executor/statement/PreparedStatementHandler.query [ps.execute();]

–> io/shardingjdbc/core/jdbc/core/statement/ShardingPreparedStatement.execute -> route

–> io/shardingjdbc/core/routing/PreparedStatementRoutingEngine.route [参数就是sql的参数 == final List parameters]

除了类型是select,前面这大部分和前略的insert都是类似的

[io/shardingjdbc/core/parsing/parser/sql/SQLParserFactory.newInstance return SelectParserFactory.newInstance(dbType, shardingRule, lexerEngine);]

–> io/shardingjdbc/core/routing/type/simple/SimpleRoutingEngine.routeDataSources

与前略的insert不同会走到根据分表分库的策略选库上[shardingRule.getDatabaseShardingStrategy(tableRule).doSharding(availableTargetDatabases, databaseShardingValues);] –> io/shardingjdbc/core/routing/strategy/standard/StandardShardingStrategy.doSharding [参数中有所有待选的数据库:final Collection availableTargetNames,另外还有逻辑表名(例如:配置的没有后缀数字的表名)和列以及参数:List

]

shardingValue instanceof ListShardingValue判断(如果是就循环,不是就直接走分库逻辑)后会走进自定义的分库策略逻辑,例如: 中的配置。 对已有分片键值使用自定义的分片算法选出所有符合自定义算法的数据库

« each : routeTables

从所有数据源[库.表]中选出刚刚策略计算出的库中对应的所有表。当分表键存在时,对这些表使用自定义分表策略(与上面分库逻辑基本一样),键不存在直接使用刚选出的库中所有匹配的表。再用这些库和表创建DataNode。

简单版的select后面没说的就都参见上面的前略吧,下面看个复杂的。

上面简单版解析sql的部分都跳过了,这里看一下(以下所有解析出来的全都放在一个sqlStatement对象里了): io/shardingjdbc/core/routing/PreparedStatementRoutingEngine.route

–> io/shardingjdbc/core/routing/router/ParsingSQLRouter.parse

–> SQLParsingEngine.parse [SQLParserFactory.newInstance(dbType, lexerEngine.getCurrentToken().getType(), shardingRule, lexerEngine) == SQLParser == MySQLSelectParser]

–> io.shardingjdbc.core.parsing.parser.sql.dql.select.AbstractSelectParser.parse -> parseInternal

–> io/shardingjdbc/core/parsing/parser/dialect/mysql/sql/MySQLSelectParser.parseInternal

-> parseDistinct不支持Distinct

-> parseSelectOption [io/shardingjdbc/core/parsing/parser/dialect/mysql/clause/MySQLSelectOptionClauseParser.parse]

-> parseSelectList [io/shardingjdbc/core/parsing/parser/dialect/mysql/sql/MySQLSelectParser.parse -> parseSelectItem:分别处理不同类型的列,如行号、*、聚合函数(Count等,要处理括号,以及计算如count()+等数值计算)和正常情况(parseCommonSelectItem拼不同列的列名或别名)]

-> parseFrom [ 不支持INTO,parseTable(如果跟着左括号,处理子查询) –> io/shardingjdbc/core/parsing/parser/clause/TableReferencesClauseParser.parse –> MySQLTableReferencesClauseParser.parseTableReference和前略里的处理差不多,不同的就是这里有别名AS和连表,连表还要处理ON和USING的连接条件]

-> parseWhere [io/shardingjdbc/core/parsing/parser/clause/WhereClauseParser.parse -> parseConditions -> parseConditions 条件是分左右两个SQLExpression对象存在condition里的,这里判断了OR目前是不支持的]

-> parseGroupBy [io/shardingjdbc/core/parsing/parser/clause/GroupByClauseParser.parse]

-> parseHaving [不支持having]

-> parseOrderBy [io/shardingjdbc/core/parsing/parser/clause/OrderByClauseParser.parse 默认排序是ASC]

-> parseLimit [io/shardingjdbc/core/parsing/parser/dialect/mysql/clause/MySQLLimitClauseParser.parse]

-> parseSelectRest [io/shardingjdbc/core/parsing/parser/clause/SelectRestClauseParser.parse 一些不支持的关键字:UNION,INTERSECT,EXCEPT,MINUS,PROCEDURE,INTO]

« io/shardingjdbc/core/parsing/parser/sql/dql/select/SelectStatement [containsSubQuery, 判断是否包含子查询,如果包含,执行mergeSubQueryStatement]:

-> appendDerivedColumns [和appendDerivedOrderBy有todo注释(move to rewrite),处理聚合函数,如遇到avg会增加count和sum函数列到avg的derived项中,并以AVG_DERIVED_COUNT/SUM_+ derived项序号等形式做别名]

« appendDerivedOrderColumns [根据参数不同分别为order和group项加别名]

«

–> io/shardingjdbc/core/routing/router/ParsingSQLRouter.route [取出逻辑表名,构造ComplexRoutingEngine路由引擎对象]

–> io/shardingjdbc/core/routing/type/complex/ComplexRoutingEngine.route

[在ShardingRule中找对应逻辑表的规则配置,并对单个逻辑表构建SimpleRoutingEngine对象,执行route方法(此处参见上面那个简单的SQL)]

« getDataSourceLogicTablesMap返回数据源交集共有的逻辑表 -> getIntersectionDataSources这个方法对数据源取交集返回,取交集就是说同一个CartesianTableReference中的tableUnits都应该在同一个数据库,ComplexRoutingEngine.route所有库的逻辑表对应真实库和表对应关系List放到CartesianRoutingResult对象实例中]

–> io/shardingjdbc/core/routing/type/complex/CartesianRoutingEngine.route [用join的表构造笛卡尔组合的TableUnits的Set,放在RoutingResult的routingTableReferences中返回]

«

-> processLimit

« io/shardingjdbc/core/rewrite/SQLRewriteEngine.rewrite [用sqlToken构建SQLBuilder对象实例]

«

«

« handleResultSets(ps);]

–> org/apache/ibatis/executor/resultset/DefaultResultSetHandler.handleResultSets -> getFirstResultSet [stmt.getResultSet()]

–> «

–> io/shardingjdbc/core/merger/MergeEngine.merge

-> build [合并结果集 GroupByStreamResultSetMerger > OrderByStreamResultSetMerger > IteratorStreamResultSetMerger这里顺序会有影响,前面的resultSetMerger会传给后面,在最后mybatis拼装结果集时会被类似链式的方式调用] -> decorate [处理limit, LimitDecoratorResultSetMerger]

«

«

-> handleResultSet -> handleRowValues

-> handleRowValuesForSimpleResultMap [此处DefaultResultSetHandler的Limit没什么用是最开始DefaultSqlSession处的RowBounds.DEFAULT,循环的判断条件中rsw.getResultSet()是返回ShardingResultSet,我的这个例子next方法:return mergeResultSet.next() –> LimitDecoratorResultSetMerger.next 此处控制从多数据源返回的结果集合并后只返回前Limit行记录,这里Limit还会调用前面OrderByStreamResultSetMerger的next] each :

-> getRowValue -> applyAutomaticMappings [向元数据对象里填查询回来的结果值]

-> storeObject -> callResultHandler [结果放进泛型对象实例]

后面似乎也没什么关系了。

备注:OrderByStreamResultSetMerger.next:

公众号:

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20180323G1VZ3X00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

同媒体快讯

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励