本文主要描述分库分表的算法方案、按什么规则划分。循序渐进比较目前出现的几种规则方式,最后第五种增量迁移方案是我设想和推荐的方式。后续章再讲述技术选型和分库分表后带来的问题。
背景
随着业务量递增,数据量递增,一个表将会存下大量数据,在一个表有一千万行数据时,通过sql优化、提升机器性能还能承受。为了未来长远角度应在一定程度时进行分库分表,如出现数据库性能瓶颈、增加字段时需要耗时比较长的时间的情况下。解决独立节点承受所有数据的压力,分布多个节点,提供容错性,不必一个挂整个系统不能访问。
目的
本文讲述的分库分表的方案,是基于水平分割的情况下,选择不同的规则,比较规则的优缺点。
一般网上就前三种,正常一点的会说第四种,但不是很完美,推荐我认为比较好的方案五。
- 方案一:对Key取模,除数逐步递增
- 方案二:按时间划分
- 方案三:按数值范围
- 方案四:一致性Hash理念——平均分布方案(大众点评用这种,200G并且一步到位)
- 方案五:一致性Hash理念——按迭代增加节点(为了方便增量迁移)
方案选择
方案一:对Key取模,除数逐步递增
公式:key mod x (x为自然数)
Key可以为主键,也可以为订单号,也可以为用户id,这个需要根据场景决定,哪个作为查询条件概率多用哪个。
优点:
- 按需增加库、表,逐步增加
- 分布均匀,每一片差异不多
缺点:
- 很多时候会先从2开始分两个库逐级递增,然后分3个、4个、5个。如在mod 3 变 mod 5的情况下,取模后的大部分的数据的取模结果会变化,如key=3时,mod 3=0,突然改变为mod 5=3,则将会从第0表迁移到第3表,将会造成很多数据多重复移动位置。
- 会重复迁移数据,当分2个时,有数据A在第0号表,分3个时数据A去了第1号表、到分4个时数据A会回到第0号表
方案二:按时间划分
可以按日、按月、按季度。
tb_20190101
tb_20190102
tb_20190103
……
这个算法要求在订单号、userId上添加年月日或者时间戳,或者查询接口带上年月日,才能定位在哪个分片。
优点:
- 数据按时间连续
- 看数据增长比较直观
缺点:
- 因为考虑到历史数据一开始没分库分表后续进行分库分表时,历史数据的订单号不一定有时间戳,历史数据可能为自增或者自定义算法得出的分布式主键,导致查询时必须要上游系统传订单号、创建时间两个字段。
- 若上游系统没有传时间,或者上游系统的创建时间与当前系统对应订单的创建时间不在同一天的情况下,则当前数据库表的数据记录需要有时间字段。因为上游系统只传订单号,这个时候需要获取创建时间,当前系统就必须要有一个主表维护订单号和创建时间的关系,并且每次查询时都需要先查当前系统主表,再查具体表,这样就会消耗性能。
- 分布不一定均匀:每月增长数据不一样,可能会有些月份多有些月份少
推荐使用场景:日志记录
方案三:按数值范围
表0 [0,10000000)
表1 [10000000,20000000)
表2 [20000000,30000000)
表3 [30000000,40000000)
……
优点:
- 分布均匀
缺点:
- 因为未知最大值,所以无法用时间戳作为key,这个方法不能用表的自增主键,因为每个表都自增数量不是统一维护。所以需要有一个发号器或发号系统做统一维护key自增的地方。