sharding-jdbc-how2work 当当的sharding-jdbc剖析
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了sharding-jdbc-how2work 当当的sharding-jdbc剖析相关的知识,希望对你有一定的参考价值。
> 空开 否则右边挡了,好不优雅的办法
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
JDBC出发点
整体思路:实现JDBC规范的接口 分析com.dangdang.ddframe.rdb.sharding.jdbc包
实现javax.sql.DataSource接口 ShardingDataSource
实现java.sql.Connection接口 ShardingConnection
实现java.sql.Statement ShardingStatement
实现java.sql.PreparedStatement ShardingPreparedStatement
实现java.sql.ResultSet AbstractShardingResultSet
重新实现的方式
AbstractUnsupportedOperationXXXX描述不支持的接口实现 通过这一系列的类可以看出不支持哪些操作
AbstractXXXXAdapter 这一层意义?
WrapperAdapter
具体实现分析
ShardingDataSource
根据ShardingRule实例准备DatabaseMetaData实例 用于构建ShardingConnection实例
准备DatabaseMetaData实例的过程: 根据原有Datasource获取到数据库产品的信息 一次准备的所有数据源必须是同一种数据库产品 不能既有MySql又有DB2
ShardingConnection
主要完成createStatement、prepareStatement接口的实现
实现createStatement、prepareStatement接口 主要过程是创建ShardingStatement、ShardingPreparedStatement实例 创建上述两实例需要SQLRouteEngine实例
SQLRouteEngine实例实在connection构造的时候完成创建 需要ShardingRule、DatabaseType
SQLRouteEngine干什么用的呢?用于分库分表的规则逻辑的接入点
ShardingStatement
实现java.sql.Statement接口 不支持的操作参见AbstractUnsupportedOperationStatement
currentResultSet = ResultSetFactory.getResultSet(generateExecutor(sql).executeQuery(), mergeContext);
ShardingPreparedStatement
AbstractShardingResultSet
Executor
根据SQLRouteEngine与sql 组装出StatementExecutor
route出SQLRouteResult
SQLRouteResult拿到执行单元SQLExecutionUnit 执行单元包装了sql及其相应的dataSource的key名
拿到MergeContext
StatementExecutor本质在于包装了多个Statement及相应sql 在代码中用StatementEntity做了表示 这里的sql已经是用物理表名置换了逻辑表名后的sql 这里的Statement已经是相应jdbc driver对应的原生的Statement
SQLRouteEngine
1. route是其核心的对外暴露的接口
parseSQL接口用SQLParseEngine parse出SQLParsedResult
根据parse出的SQLParsedResult结果实例中的 ConditionContext、逻辑表名、SQLBuilder实例 与shardingRule(SQLRouteEngine的实例变量) 加工出SQLExecutionUnit实例列表
1.1 parseSQL
1. SQLParserFactory根据逻辑sql(还没有实际的物理表名的sql)、分片列名、数据库类型、查询的条件参数创建SQLParseEngine
1.1 创建SQLParseEngine主要过程有: a. 用阿里的druid解析出语法树com.alibaba.druid.sql.ast.SQLStatement b. 根据sql语句是增删改查来决定用哪种visitor实例(用于后置处理)
1.2 parse过程 依赖visitor 含有or条件的特殊处理
具体的visitor
TODO or条件的特殊处理
1.2 route
需要的要素:shardingRule(SQLRouteEngine的实例变量), ConditionContext,逻辑表名,SQLBuilder实例
又分三种: SingleTableRouter--route-->SingleRoutingResult //单表(逻辑表)操作 BindingTablesRouter--route-->BindingRoutingResult MixedTablesRouter--route-->RoutingResult(CartesianTablesRouter)
SingleTableRouter
routeDataSources
ShardingValue DatabaseShardingStrategy.doSharding
routeTables
ShardingValue TableShardingStrategy.doSharding
BindingTablesRouter
BindingRoutingResult.bind
BindingRoutingDataSource.bind
BindingRoutingTableFactor
visitor
五种。 看懂visitor前提是要了解 编译器常用的访问者这种设计模式
com.dangdang.ddframe.rdb.sharding.parser.visitor.basic.mysql MySQLInsertVisitor MySQLDeleteVisitor MySQLSelectVisitor MySQLUpdateVisitor OrVisitor
visitor通过ParseContex 做了mergeCurrentConditionContext、getParsedResult、 visitor还getSQLBuilder
AbstractMySQLVisitor 增删改查都用到的 重新实现visit以下类型AST节点的逻辑: SQLVariantRefExpr,SQLExprTableSource,SQLPropertyExpr, SQLBinaryOpExpr,SQLInListExpr,SQLBetweenExpr
SQLVariantRefExpr sql中的问号占位符,:xxx这种命名参数, @max_order_id:= 这种 select @max_order_id:=max(order_id) from order where id=? and user_id=:userId
父类不仅用@@ 替换?, 同时用实际参数值替换了?占位符 sharding-jdbc将占位符换成真正参数值的 动作交由ShardingPreparedStatement完成
SQLExprTableSource select * from order o where o.id=1 from后面的order就是SQLExprTableSource select * from (select * from order o where o.id=1) order_alias from后面的order是SQLExprTableSource,但order_alias不是 order_alias 是SQLSubqueryTableSource SQLSubqueryTableSource与SQLExprTableSource 都是SQLTableSourceImpl的子类
插入parseContext.addTable(x)逻辑: 1. 处理表名及其别名中特殊字符--> []`‘\\ 2. 将表名及其别名加入RouteContext
SQLPropertyExpr select o.name from order o where o.id=1 此处的name和id都是SQLPropertyExpr 接在表别名点号后面的是SQLPropertyExpr
针对"select * from T_ORDER where T_ORDER.order_id=1002 这种sql重新实现此类型节点的visitor 过程:将T_ORDER.order_id换成别名t_order.order_id 其余的SQLPropertyExpr沿用父类逻辑
SQLBinaryOpExpr select * from order o where o.id=1 左值o.id 右值 1 就是描述运算的 select 1+1 1+1也是
1. 发现有or操作时, 要将parseContext中存在or条件标志置成true 便于后面特殊处理 2. 发现有=(等于比较)操作时将其加入到 parseContext的ConditionContext中
SQLInListExpr select * from user u where u.id in (1,3,4,5) targetList是in后面的列表 expr是in前面的表达式
in操作与上面=操作的处理相同
SQLBetweenExpr select * from t_order_0 where order_id between 1004 and 1008
between与上面=操作相同
MySQLSelectVisitor 重新实现/更新visit以下类型AST节点的逻辑: MySqlSelectQueryBlock,SQLSelectItem,SQLAggregateExpr SQLOrderBy,MySqlSelectGroupByExpr, MySqlSelectQueryBlock.Limit, endVisit(final SQLSelectStatement x)
MySqlSelectQueryBlock 插入逻辑: 告诉程序当前在处理哪个表的子查询
SQLSelectItem 插入逻辑: 更新itemIndex给处理SQLAggregateExpr时使用
SQLAggregateExpr 聚合: String[] AGGREGATE_FUNCTIONS = { "AVG", "COUNT", "MAX", "MIN", "STDDEV", "SUM" }
对分库分表时的聚合插入逻辑处理 主要是分析出是哪种聚合 聚合的哪一列 然后扔进ParseContext的MergeContext
使用逻辑表名查询时,聚合计算是不分片的 使用物理表名查询时, 聚合计算是报错的,sharding不认识物理表名
SQLOrderBy
类似SQLAggregateExpr的处理 对分库分表时的order by插入逻辑处理 主要是分析出是哪种排序形式 是哪一列(支持多列) 然后扔进ParseContext的MergeContext
MySqlSelectGroupByExpr
类似SQLAggregateExpr、SQLOrderBy的处理
MySqlSelectQueryBlock.Limit
处理ParseContext的MergeContext的部分 类似SQLAggregateExpr、SQLOrderBy的处理 context中的offset与rowcount是原有sql中真是的值
多一个逻辑: 到实际物理表查询的sql在此处被改写了, offset从0开始,rowcount是原有sql里的offset加上原有sql里的rowcount 当然同时支持(兼容)?占位符的情况
日志更直观: Logic SQL: select * from t_order o order by o.order_id limit 1,2 route sql to db: [db0] sql: [SELECT * FROM t_order_0 o ORDER BY o.order_id LIMIT 0, 3] route sql to db: [db0] sql: [SELECT * FROM t_order_1 o ORDER BY o.order_id LIMIT 0, 3]
endVisit(final SQLSelectStatement x)
当一个查询结束后 从MergeContext拿到AggregationColumns 进行加工,举例,主要逻辑如下: select avg(order_id) from t_order 加工成 SELECT AVG(order_id)[Token(, COUNT(order_id) AS sharding_gen_1, SUM(order_id) AS sharding_gen_2)] FROM [Token(t_order)] 主要是补了sum和count
MySQLDeleteVisitor
更新MySqlDeleteStatement处理逻辑 将当前表名及其别名加入RouteContext 感觉跟AbstractMySQLVisitor里对SQLExprTableSource 处理逻辑有重复部分
MySQLInsertVisitor
将当前表名及其别名加入RouteContext 同delete
插入的列也需要纳入ParseContext的ConditionContext管理 以保证插入到正确的表中!
MySQLUpdateVisitor
将当前表名及其别名加入RouteContext 同delete
没有其他处理逻辑了 那么分片键再次更新就不能正确的落到相应的物理表中了 分片键值更新 分片键还作为update的where条件 update t_order_0 set order_id=1011 where order_id=1002
以上是关于sharding-jdbc-how2work 当当的sharding-jdbc剖析的主要内容,如果未能解决你的问题,请参考以下文章