分库分表的学习与实践

通常来说,数据库是比较容易成为系统瓶颈的,因为它本身是有状态的,相对于 Web 和应用服务器来说,比较难于横向扩展。而数据库的单机存储量、连接数和处理能力都是有限的,因此当一个数据库的单表数据达到一定的量级后,即使进行多种优化,比如添加从库、优化索引等,可能性能仍然下降严重,此时就需要考虑对其进行分库分表的操作了。

分库分表是拆分数据库和拆分数据表的统称,一般情况下会根据业务情况来决定是分库还是分表,或者是二者相结合。而根据拆分方式的不同,又分为垂直拆分和水平拆分。

垂直分表

垂直分表的通常情况就是:由于某个表中字段较多,我们可以通过新建一张扩展表,将不经常使用或者长度较大的字段拆分出去放到扩展表中。这种做法可以使日常的开发和维护更加方便,并且在某些数据库(比如 MySQL、SQL Server 等)中还可以避免跨页的问题。但是缺点也很明显:主键出现冗余,在某些查询时可能需要 JOIN 操作,并且事务处理也变得复杂了。垂直分表的操作一般在数据库的设计阶段就应该做好,如果在后续阶段中进行拆分,很有可能需要改写以前的查询语句,造成额外的麻烦。

垂直分表

垂直分库

垂直分库就是将数据库中关系紧密的一些表拆分出来放到一个独立的数据库服务中,这些关系紧密的表在业务上通常也是关系紧密的,因此垂直分库一般需要业务层面上的模块划分,比如将某个系统划分成用户模块、订单模块和商品模块等。

因为微服务的盛行,垂直分库在今天已经非常普及了。在高并发和海量数据的场景下,垂直切分可以从一定程度上突破单机数据库的 I/O、连接数等硬件资源的瓶颈。但是垂直分库的缺点也有很多,比如可能面临跨库 JOIN 查询,这时需要通过接口聚合的方式将数据在内存中完成关联,复杂度提高。如果某一类型的业务骤增,会造成对应业务的数据库压力增大,可能还会面临单机数据库的性能瓶颈问题。与此同时,跨库事务的一致性,也就是分布式事务处理起来也很麻烦。

垂直分库

水平分表

水平分表就是将表中不同的数据行按照一定的规律拆分到不同的表中,这种做法可以降低单表的数据量,优化查询性能,但是由于所有的表都存储在同一个库中,所以还是会面临单机数据库的性能瓶颈问题。

水平分库分表

水平分库分表与水平分表的思路相同,唯一的区别就是水平分库分表需要将拆分出来的表存放在不同的数据库服务中。在高并发和海量数据的场景下,水平分库分表可以有效地缓解单机数据库的性能问题,但是与此同时也引入了很多复杂的技术难点,接下来会详细分析。

分片策略

分片策略决定的是按照怎样的规则将表中不同的数据行进行拆分,常见的分片策略有 RangeHash + Mod 等。

Range

Range 按照区间划分表中不同的数据行,可能会是时间区间、ID 区间等。比如我们可以按照日期字段将不同年份月份甚至是每天的数据行划分到不同的库中,或者根据 ID 值的范围将 1 到 10000 的记录划分到第一个库,将 10001 到 20000 的记录划分到第二个库,以此类推。

时间区间

这种划分方式的优点在于:其天然便于水平扩展,如果后期需要对集群进行扩容,只需要增加节点即可,无需对其他分片的数据进行迁移。在使用分片字段进行范围查找时,连续的分片可以快速地定位和查询,有效避免跨分片的问题。当然缺点也同样存在:某些分片可能会在某个时间段内被大量的读写,从而造成数据分布不均匀,热点数据成为性能瓶颈。

如果我们选择具有周期性的区间进行数据划分,那么在需要扩容时,不可避免的需要进行数据的迁移。比如,按照日期字段将不同月份的数据划分到不同的库中,即 monthOfYear,那么共有 12 个库,在下次需要扩容时,可能需要按照日期字段将不同日期的数据划分到不同的库中,即 dayOfMonth,那么可能共有 31 个库,这时就需要进行数据迁移。

Hash + Mod

Hash + Mod 就是对所有的数据库节点进行编号,比如一共有 4 台数据库服务节点,则编号 0 ~ 3,然后对原始数据行中某个字段的值进行一次 Hash 运算,结果需要是一个整型数值,然后对这个整型数值取模 Mod,得到的值就是对应数据库的编号。

这种分片策略中的分片字段一般会选择 ID 字段,因为对唯一字段的值进行 Hash 运算,结果会比较均匀分散,这样不容易出现热点数据的问题。

Hash+Mod

这种划分方式可以有效避免热点数据的产生,但是比较容易面临跨分片查询的问题,并且在后期扩容时还会面临数据迁移的问题。比如初期共有 4 台服务器,数据行需要经过 Hash % 4 得到节点值,如果后期又增加了 3 台服务器,那么数据行需要重新经过 Hash % 7 计算节点值,而这个值很有可能会与开始计算的值不同,因此需要进行数据迁移。如果使用一致性 Hash 算法,那么可以避免大量的数据迁移,只需要迁移小部分数据即可

分库分表面临的问题

不管是分库还是分表,是垂直切分还是水平切分,都会面临一系列的问题,这些问题有的可以通过设计时的调整解决,有的可能需要引入更加复杂的工具来处理。

跨节点 JOIN