大表治理(三)-分库分表

179 阅读4分钟

大表治理(一)-常见方案
大表治理(二)-归档
大表治理(三)-分库分表
大表治理(四)-分库分表中间件Mycat 和Sharding-Jdbc简介

什么是分库分表

通过字面意思就很好理解:

  • 分库:从单个数据库拆分成多个数据库的过程,将数据散落在多个数据库中。
  • 分表:从单张表拆分成多张表的过程,将数据散落在多张表内。

如何分库分表

分表策略选择

  1. Hash 即对分表键进行hash取模
  2. Range 即按ID段进行分表,1-1000万是一张表,1000万-2000万是一张表
  3. 时间 如1月份是一张表,2月份是一张表

分库分表执行步骤

通用的解决方案如下:

  • 第一步:新旧库双写
  • 第二步:将老库中的数据复制到新库中
  • 第三步:新老库数据一致性校验
  • 第四步:逐步切读流量到新库
  • 第五步:停写旧库

分库分表面临的问题

分布式事务问题

当写入或者更新内容同时分布在不同库中,不可避免会带来跨库事务问题。跨分片事务也是分布式事务,没有简单的方案,一般可使用"XA协议"和"两阶段提交"处理。

分布式事务能最大限度保证了数据库操作的原子性。但在提交事务时需要协调多个节点,推后了提交事务的时间点,延长了事务的执行时间。导致事务在访问共享资源时发生冲突或死锁的概率增高。随着数据库节点的增多,这种趋势会越来越严重,从而成为系统在数据库层面上水平扩展的枷锁。

还有一个比较好的方案是通过补偿去达到最终一致性来解决分布式事务。

跨节点分页查询问题

当用户在订单列表中查询所有订单时,可以通过usrId的Hash值来快速查询到订单信息,而运营人员在后台对订单表进行查询时,则是通过订单付款时间来进行查询的,这些数据都分布在不同的库以及表中,此时就存在一个跨节点分页查询的问题了。

通常一些中间件是通过在每个表中先查询出一定的数据,然后在缓存中排序后,获取到对应的分页数据。这种方式在越往后面的查询,就越消耗性能。

通常我们建议使用两套数据来解决跨节点分页查询问题,一套是基于分库分表的用户单条或多条查询数据,一套则是基于Elasticsearch、Solr存储的订单数据,主要用于运营人员根据其它字段进行分页查询。为了不影响提交订单的业务性能,我们一般使用异步消息来实现Elasticsearch、Solr订单数据的新增和修改。

全局主键ID问题

在分库分表后,主键将无法使用自增长来实现了,在不同的表中我们需要统一全局主键ID。因此,我们需要单独设计全局主键,避免不同表和库中的主键重复问题。

  1. UUID
  2. 结合数据库维护主键ID表
  3. Snowflake分布式自增ID算法

分库分表的JOIN

先说下观点:能去掉JOIN的话就去掉JOIN
实在去不掉可根据业务实际情况采用下面方式解决

  1. 同逻辑拆分,适用于几个表分表逻辑一样,这样所有的join都在同一个数据库里面。
  2. 小表广播,适用一些小表,每个分库上都有同步小表的全量数据。

非分表键的查询

  1. 多维归一维 如order表会使用userId,orderNo查询,但是分表键只有userId,但是生成orderNo时,其中有6位是userId,这样通过orderNo查询时,就可以解析出userId,然后再根据userId查询
  2. 新建一个索引表 如order表会使用userId,orderNo查询,但是分表键只有userId,那我们可以建一个order_index表,里面只有userId和orderNo两个字段,通过orderNo查询时,可以先查询order_index获取分表键userId,然后再根据userId查询。
  3. 冗余分库分表 如order表会使用userId,orderNo查询,那我们就分别按userId和orderNo进行分库分表,冗余多份订单数据。
  4. 全表扫描聚合 即查询所有的分表,然后聚合查询数据。这个不常用,只适用于查询量小且性能要求不高的场景。

分库分表中间件

  • sharding-jdbc(当当)
  • TSharding(蘑菇街)
  • Cobar(阿里巴巴)
  • MyCAT(基于Cobar)