谈谈为什么要分库分表?
前言
由于数据库的承载能力是有限的,当业务增长量达到一定规模后,数据库的性能就会达到瓶颈。于是产生了分库分表的解决方案,本文将详细讲解什么是分库分表,以及分库分表的原因和可能产生的问题。
一、为什么要分库分表?
1. 分库的原因
数据库的关键性能指标主要有磁盘空间、内存、CPU。在数据库配置无法升级的情况下,当数据量过大可能产生如下的一些问题:
- 磁盘空间不足,数据库将无法进行增删改等操作;
- CPU 或内存不足,则可能导致读写性能低产生慢 SQL,进而导致数据库链接池被打满、内存溢出等问题。
这时就需要进行分库操作,来保证数据库的稳定和性能。
2. 分表的原因
由于数据库的承载能力是有限的,当业务增长量达到一定规模后,数据库的性能就会达到瓶颈。就拿使用最多的 MySQL 来进行说明。
MySQL 的默认引擎是 InnoDB,数据是存在聚簇索引中,而聚簇索引的底层数据结构是 B+ Tree,即使 B+Tree 已经在 B Tree 的基础上优化了空间利用率,但是随着数据规模的增大,树的高度也会达到一定规模,高度能够决定磁盘IO的次数,磁盘IO次数越多,对于性能就会越低。
3. 微服务架构
现在大部分公司的系统架构都已是分布式系统和微服务架构,根据公司的业务划分,分解为功能单一的数据库。此时会需要进行分库分表操作。
二、数据量多大才需要分库分表?
如下图,根据阿里开发手册中描述,单表超 500 万行,或容量超 2GB 时推荐分库分表。但是具体的情况还是需要根据业务需求、数据库硬件配置、CPU 性能等方面综合评估。
除此之外,要想详细的估算出数据量的瓶颈,还需要根据表的结构以及索引情况,表的字段不同的类型占用的大小也不同,故每条数据占用的大小也不同, B+Tree 每一层的存储数据量的大小也会发生变化。
综上,数据量多大需要根据自身情况去评估,并不一定是网上所说的 1000 万,也不一定是阿里所说的大于 500 万,这就是有些企业单表数据即使超过几千万仍可稳定运行的原因。
三、什么是分库分表?
1. 分库
将数据库中的表按照某种规则拆分到多个数据库中,来保证系统的稳定和性能。
如图,将 DataBase 中的 2 张表拆分成 trade 库和 acount 库,分别存 order 表和 acount 表。
2. 分表
将表中数据按照某种规则拆分到多张表中,提升查询效率。
如图,将 order 表拆分成 order_shanghai 和 order_beijing,分别存上海和北京的订单数据。
四、如何分库分表?
分库分表的核心就是数据切分(Sharding),以及切分后对数据的快速定位与结果整合,从而到提升数据库操作性能的目的。分库分表可以从垂直和水平两种纬度进行拆分。
1. 垂直拆分
垂直拆分包含垂直分库和垂直分表。
1.1 垂直分库
垂直分库指的是,将某个库中的表拆分到多个库,每个库包含的表不一样。一般主要是按照业务维度进行拆分,不同的业务数据放到对应的数据库中,核心理念是转库专用,这也是微服务拆分的一个重要依据。
如图,在电商系统中,主要包含的业务有商品、用户、支付、库存等,在对数据库进行划分时,每个业务对应一个数据库。
垂直分库优点:
- 降低单数据库服务的压力,增加系统可用性;
- 业务清晰,各系统间解耦合;
- 提升 IO 性能,增加数据库链接数以及其他硬件的瓶颈。
垂直分库缺点:
- 不同库之间的数据一致性无法保证,需要通过分布式事务;
- 依然存在单库、单表数据过大的问题,需要结合水平拆分;
1.2 垂直分表
垂直分表指将存在一张表中的不同字段拆分为多张表,是一种大表拆小表的模式。拆分后的表字段、结构均不同,组合一起则是原表。
一般是将经常修改的、数据较大的、不常查询的字段拆分到“扩展表”中,这样拆分以后核心表大多是访问频率较高的字段,而且字段长度也都较短,减少了查询时的磁盘 IO,提升数据库效率。
如下图,我们在进行下单时一般需要存储快照信息,该字段有长且不常使用,故拆分出来可提高订单信息的查询效率。
垂直分表优点:
- 降低表的数据规模,提升效率;
- 减少单条数据占用空间,减少磁盘 IO。
垂直分表缺点: 依然存在单表数据过大的问题,需要结合水平拆分;
2. 水平拆分
水平拆分包含水平分库和水平分表。
2.1 水平分库
水平分库是将数据库中的表数据按照某种规则拆分到多个库中,以实现水平扩展,提升数据库读写性能。
一般切分到不同的库中的表结构是相同的,表中数据不同,且伴随水平分表。
如下图,将DB-trade
库中的 order 表水平切分到DB-trade 1
和DB-trade 2
库中。
水平分库优点:
- 极大降低单库数据规模,提高系统高并发能力;
- 业务代码改动小,不需要拆分业务。
水平分库缺点:
- 跨库的 join 关联查询性能较差,增加开发复杂度;
- 单库的自增 ID 受影响。
2.2 水平分表
水平分表是将数据按照某种规则拆分到多张表,多张表还是属于一个库且结构相同。大大减少单表数据量,提升查询效率,但是对数据库的压力没有帮助。
如下图,一张 order 订单表有 400 万数据,经过水平拆分出来 2 个表 order_1 和 order_2,每张表存有数据 200万,以此类推。
水平分表优点:
- 大大减少单表数据量,提升查询效率;
- 业务代码改动小,不需要拆分业务。
水平分表缺点: 多张表属于一个库,数据库的压力仍存在。
五、分库分表可能有哪些问题?
1. 事务问题
分库可能导致一次事务的执行的数据不在同一个服务器上,数据库层面无法实现所有服务的事务操作。可能会产生数据不一致的情况。
解决方案是通过分布式事务,来能最大限度保证了数据库操作的原子性,详情见我的另一篇文章分布式事务。
2. 主键唯一性问题
在数据库表设计时,经常会使用自增ID作为数据主键,这就导致后续在迁库迁表、或者分库分表操作时,会因为主键的变化或者主键不唯一产生冲突。
解决方案是通过一些策略生成全局唯一的分布式 ID,详情见我的另一篇文章分布式 ID。
3. 跨库多表join问题
拆分之前,应用中很多列表或详情的数据可以通过 join 来完成,但是分库拆分后,数据可能分布在不同的服务器上,这种跨节点的 join 会有很多问题。
解决方案是避免挂库多表 join 的产生:
- 可以采用多次查询业务层进行数据组装;
- 将存在关联关系的表拆分在同一数据库下;
- 字段冗余避免 join 查询。
4. 跨库聚合查询问题
跨库进行聚合查询时,如 group by,order by 等,会变的异常复杂,导致常需要复杂的业务代码才能实现上述业务逻辑。
解决方案有:
- 业务代码中分别查询,然后组装数据(可能需要用一些排序算法优化效率);
- 使用专业的分布式框架,比如开源框架:ElasticSearch;
结语
本文主要讲述的是我们在开发过程中为什么需要分库分表,以及分库分表的场景、方案和可能出现的问题。因为一些内容的扩展点还有很多,放在一起影响阅读,本文只是简单的讲述了分库分表的一些基础知识,后续会针对分库分表分片方案、常用中间件的原理和使用,以及如何完整落地一个分库分表方案进行开展。
参考: