添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

分库分表

以什么维度分库分表?

分表键需要考虑实际的业务场景,比如TO C的业务一般可以uid作为分表键,TO B业务常用orgId。
还有一些场景需要支持多种方式查询,可以采用叫“基因法”的方式来分表[1]。

ShardingJDBC

事务

柔性事务

2.0 后提供柔性事务支持,执行事务前先发消息给一个 EventBus,失败后由 EventBus 负责重试。

TCC 模式

3.0 后借助 Seata 提供 TCC 模式的分布式事务。

源码分析

启动

  1. 数据源元数据信息和表元数据信息的收集
  2. 表分库分表策略和算法的配置信息收集
  3. ShardingDataSourceFactory#createDataSource 创建数据源 ShardingDataSource 实例
    -> ShardingDataSource#ShardingDataSource 创建 ShardingContext,其持有 ShardingRule ShardingMetaData 两个属性,根据一个表以及这个表的列可以从 ShardingRule 中获取这个表的分库分表策略和算法,ShardingMetaData 则维护了数据源和表的元数据信息

    ShardingJDBC 如何嵌入 MyBatis

    ShardingJDBC 接入 MyBatis 的原理是 DataSource 的替换,调用链如下:
    org.apache.ibatis.executor.SimpleExecutor#prepareStatement
    -> SimpleExecutor#getConnection 从 transaction 获取连接
    -> SpringManagedTransaction#getConnection 可以看到成员变量 dataSource 是 ShardingDataSource
    -> ShardingDataSource#getConnection 得到 ShardingConnection
    -> ShardingConnection#prepareStatment 得到 ShardingPreparedStatement

    路由及改写引擎

    ShardingPreparedStatement#executeQuery、executeUpdate、execute
    -> ShardingPreparedStatement#shard
    -> BaseShardingEngine#shard
    -> PreparedQueryShardingEngine#route
    -> PreparedStatementRoutingEngine#route ParsingSQLRouter 使用 四个引擎 对 sql 进行解析和重写
    -> ParsingSQLRouter#parse
    -> SQLParsingEngine#parse 使用 SQLParsingEngine 解析 sql,返回 SQLStatement 作为解析的结果
    -> SQLParserFactory#newInstance 获取 SQLParser 实例,如果是在 MySQL 中执行一个 DML 语句会匹配到 AntlrParsingEngine(Antlr 是一个开源语法分析器)
    -> AntlrParsingEngine.parse 分析 SQL,返回 SQLStatement
    -> SQLParserEngine #parse 解析 SQL 语法,生成 AST(抽象语法树)
    -> SQLSegmentsExtractorEngine #extract
    -> SQLStatementFillerEngine #fill
    -> SQLStatementOptimizerEngine #optimize
    -> ParsingSQLRouter#route
    -> OptimizeEngine#optimize 使用 OptimizeEngine 对 SQLStatement 进行优化,返回 ShardingConditions 对象
    -> RoutingEngine#route 使用 RoutingEngine 根据库表分片配置以及 ShardingConditions 找到目标库表,返回 RoutingResult 对象
    -> ShardingMasterSlaveRouter#route(SQLRouteResult sqlRouteResult)
    -> BaseShardingEngine#rewriteAndConvert
    -> SQLRewriteEngine#rewrite 使用 SQLRewriteEngine 根据路由结果重写 sql

    执行引擎

    ShardingPreparedStatement#executeQuery、executeUpdate、execute
    -> …
    -> ShardingPreparedStatement#initPreparedStatementExecutor
    -> PreparedStatementExecutor#init 把 SQLRouteResult 中的 RouteUnit 对象转换为 ShardingExecuteGroup 对象集合并从数据源获取连接和 PreparedStatement
    -> PreparedStatementExecutor#obtainExecuteGroups 根据路由的结果 SQLRouteResult 中的 RouteUnit 集合创建 StatementExecuteUnit 集合对象
    -> AbstractConnectionAdapter#getConnections 获取数据源连接
    -> getDataSourceMap().get(dataSourceName) 根据逻辑数据源名称获取真实数据源 DataSource
    -> AbstractConnectionAdapter#createConnections 从数据源获取连接 Connection
    -> getExecuteGroups().addAll() 将 StatementExecuteUnit 集合保存在 AbstractStatementExecutor 的属性 executeGroups 中
    -> AbstractStatementExecutor#cacheStatements 从连接获取 Statement 并缓存
    -> PreparedStatementExecutor#executeQuery、executeUpdate、execute
    -> AbstractStatementExecutor#executeCallback
    -> SQLExecuteTemplate#executeGroup
    -> ShardingExecuteEngine#groupExecute 使用 ShardingExecuteEngine 执行,执行 ShardingExecuteGroup
    -> ShardingExecuteEngine#parallelExecute 异步执行,如果 inputGroups 集合不止一个,则第一个同步执行、其他的异步执行

    归并引擎

    ShardingPreparedStatement#executeQuery、executeUpdate、execute
    -> …
    -> ShardingPreparedStatement#getResultSet(MergeEngine mergeEngine)
    -> AbstractStatementExecutor#getResultSets
    -> ShardingPreparedStatement#getCurrentResultSet 合并结果,返回 ShardingResultSet
    -> MergeEngine#merge 使用 MergeEngine 对结果进行合并,executeQuery()返回的是一个 List 集合,此时需要对 QueryResult 集合进行合并,MergeEngine 接口有两个实现类 DQLMergeEngine 和 DALMergeEngine,这两个实现类分别负责数据查询 sql 的合并和数据库管理 sql 的合并

    参考

    分库分表

    1. 帖子中心,1亿数据,架构如何设计?
      提到给帖子中心设计数据库结构时将tid(帖子ID)还是uid(用户ID)作为分表key,因为实际业务中既包含根据tid查询的场景又包含根据uid查询的场景,因此最终方案是采用所谓的“基因法”。
    2. ShardingJDBC

      1. 张亮:Sharding-Sphere 成长记
      2. sharding-sphere/ShardingSphereDemo
      3. Document
      4. Sharding-JDBC 源码解析
      5. antlr 解析语法树的使用