码迷,mamicode.com
首页 > 数据库 > 详细

sharding-jdbc-how2work 当当的sharding-jdbc剖析

时间:2016-02-13 00:25:45      阅读:1912      评论:0      收藏:0      [点我收藏+]

标签:

> 空开 否则右边挡了,好不优雅的办法

>

>

>

>

>

>

>

>

>

>

>

>

>

>

>

>

>

>

>

>

>

>

>

>

>

>

>

>

>

>

>

>

>

>

>

>

 

 

 

技术分享

技术分享

 

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剖析

标签:

原文地址:http://www.cnblogs.com/simoncook/p/5187357.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!