谈谈为什么要分库分表?

前言

由于数据库的承载能力是有限的,当业务增长量达到一定规模后,数据库的性能就会达到瓶颈。于是产生了分库分表的解决方案,本文将详细讲解什么是分库分表,以及分库分表的原因和可能产生的问题。


一、为什么要分库分表?

1. 分库的原因

数据库的关键性能指标主要有磁盘空间、内存、CPU。在数据库配置无法升级的情况下,当数据量过大可能产生如下的一些问题:

  1. 磁盘空间不足,数据库将无法进行增删改等操作;
  2. CPU 或内存不足,则可能导致读写性能低产生慢 SQL,进而导致数据库链接池被打满、内存溢出等问题。

这时就需要进行分库操作,来保证数据库的稳定和性能。

2. 分表的原因

由于数据库的承载能力是有限的,当业务增长量达到一定规模后,数据库的性能就会达到瓶颈。就拿使用最多的 MySQL 来进行说明。

MySQL 的默认引擎是 InnoDB,数据是存在聚簇索引中,而聚簇索引的底层数据结构是 B+ Tree,即使 B+Tree 已经在 B Tree 的基础上优化了空间利用率,但是随着数据规模的增大,树的高度也会达到一定规模,高度能够决定磁盘IO的次数,磁盘IO次数越多,对于性能就会越低。

image.png

3. 微服务架构

现在大部分公司的系统架构都已是分布式系统和微服务架构,根据公司的业务划分,分解为功能单一的数据库。此时会需要进行分库分表操作。

二、数据量多大才需要分库分表?

如下图,根据阿里开发手册中描述,单表超 500 万行,或容量超 2GB 时推荐分库分表。但是具体的情况还是需要根据业务需求、数据库硬件配置、CPU 性能等方面综合评估。

除此之外,要想详细的估算出数据量的瓶颈,还需要根据表的结构以及索引情况,表的字段不同的类型占用的大小也不同,故每条数据占用的大小也不同, B+Tree 每一层的存储数据量的大小也会发生变化。

综上,数据量多大需要根据自身情况去评估,并不一定是网上所说的 1000 万,也不一定是阿里所说的大于 500 万,这就是有些企业单表数据即使超过几千万仍可稳定运行的原因。

image.png

三、什么是分库分表?

1. 分库

将数据库中的表按照某种规则拆分到多个数据库中,来保证系统的稳定和性能。

如图,将 DataBase 中的 2 张表拆分成 trade 库和 acount 库,分别存 order 表和 acount 表。

image.png

2. 分表

将表中数据按照某种规则拆分到多张表中,提升查询效率。

如图,将 order 表拆分成 order_shanghai 和 order_beijing,分别存上海和北京的订单数据。

image.png

四、如何分库分表?

分库分表的核心就是数据切分(Sharding),以及切分后对数据的快速定位与结果整合,从而到提升数据库操作性能的目的。分库分表可以从垂直和水平两种纬度进行拆分。

1. 垂直拆分

垂直拆分包含垂直分库和垂直分表。

1.1 垂直分库

垂直分库指的是,将某个库中的表拆分到多个库,每个库包含的表不一样。一般主要是按照业务维度进行拆分,不同的业务数据放到对应的数据库中,核心理念是转库专用,这也是微服务拆分的一个重要依据。

如图,在电商系统中,主要包含的业务有商品、用户、支付、库存等,在对数据库进行划分时,每个业务对应一个数据库。

image.png

垂直分库优点:

  1. 降低单数据库服务的压力,增加系统可用性;
  2. 业务清晰,各系统间解耦合;
  3. 提升 IO 性能,增加数据库链接数以及其他硬件的瓶颈。

垂直分库缺点:

  1. 不同库之间的数据一致性无法保证,需要通过分布式事务;
  2. 依然存在单库、单表数据过大的问题,需要结合水平拆分;

1.2 垂直分表

垂直分表指将存在一张表中的不同字段拆分为多张表,是一种大表拆小表的模式。拆分后的表字段、结构均不同,组合一起则是原表。

一般是将经常修改的、数据较大的、不常查询的字段拆分到“扩展表”中,这样拆分以后核心表大多是访问频率较高的字段,而且字段长度也都较短,减少了查询时的磁盘 IO,提升数据库效率。

如下图,我们在进行下单时一般需要存储快照信息,该字段有长且不常使用,故拆分出来可提高订单信息的查询效率。

image.png

垂直分表优点:

  1. 降低表的数据规模,提升效率;
  2. 减少单条数据占用空间,减少磁盘 IO。

垂直分表缺点: 依然存在单表数据过大的问题,需要结合水平拆分;

2. 水平拆分

水平拆分包含水平分库和水平分表。

2.1 水平分库

水平分库是将数据库中的表数据按照某种规则拆分到多个库中,以实现水平扩展,提升数据库读写性能。

一般切分到不同的库中的表结构是相同的,表中数据不同,且伴随水平分表。

如下图,将DB-trade库中的 order 表水平切分到DB-trade 1DB-trade 2库中。

image.png

水平分库优点:

  1. 极大降低单库数据规模,提高系统高并发能力;
  2. 业务代码改动小,不需要拆分业务。

水平分库缺点:

  1. 跨库的 join 关联查询性能较差,增加开发复杂度;
  2. 单库的自增 ID 受影响。

2.2 水平分表

水平分表是将数据按照某种规则拆分到多张表,多张表还是属于一个库且结构相同。大大减少单表数据量,提升查询效率,但是对数据库的压力没有帮助

如下图,一张 order 订单表有 400 万数据,经过水平拆分出来 2 个表 order_1 和 order_2,每张表存有数据 200万,以此类推。

image.png

水平分表优点:

  1. 大大减少单表数据量,提升查询效率;
  2. 业务代码改动小,不需要拆分业务。

水平分表缺点: 多张表属于一个库,数据库的压力仍存在。

五、分库分表可能有哪些问题?

1. 事务问题

分库可能导致一次事务的执行的数据不在同一个服务器上,数据库层面无法实现所有服务的事务操作。可能会产生数据不一致的情况。

解决方案是通过分布式事务,来能最大限度保证了数据库操作的原子性,详情见我的另一篇文章分布式事务。

2. 主键唯一性问题

在数据库表设计时,经常会使用自增ID作为数据主键,这就导致后续在迁库迁表、或者分库分表操作时,会因为主键的变化或者主键不唯一产生冲突。

解决方案是通过一些策略生成全局唯一的分布式 ID,详情见我的另一篇文章分布式 ID

3. 跨库多表join问题

拆分之前,应用中很多列表或详情的数据可以通过 join 来完成,但是分库拆分后,数据可能分布在不同的服务器上,这种跨节点的 join 会有很多问题。

解决方案是避免挂库多表 join 的产生:

  1. 可以采用多次查询业务层进行数据组装;
  2. 将存在关联关系的表拆分在同一数据库下;
  3. 字段冗余避免 join 查询。

4. 跨库聚合查询问题

跨库进行聚合查询时,如 group by,order by 等,会变的异常复杂,导致常需要复杂的业务代码才能实现上述业务逻辑。

解决方案有:

  1. 业务代码中分别查询,然后组装数据(可能需要用一些排序算法优化效率);
  2. 使用专业的分布式框架,比如开源框架:ElasticSearch;

结语

本文主要讲述的是我们在开发过程中为什么需要分库分表,以及分库分表的场景、方案和可能出现的问题。因为一些内容的扩展点还有很多,放在一起影响阅读,本文只是简单的讲述了分库分表的一些基础知识,后续会针对分库分表分片方案、常用中间件的原理和使用,以及如何完整落地一个分库分表方案进行开展。


参考:

[1] 程序员小富. 好好的系统,为什么要分库分表?

[2] 鹅厂架构师. 一文读懂数据库分库分表.

posted @ 2024-05-16 11:32  fuxing.  阅读(1991)  评论(0编辑  收藏  举报