目录
- 1、为什么要分库分表?
- 2、切分方案有哪些?
- 2.1 分库
- 2.1.1 垂直分库
- 2.1.2 水平分库
- 2.2 分表
- 2.2.1 垂直分表
- 2.2.2 水平分表
- 2.3 分库分表
- 3、数据水平分片方法
- 3.1 Hash分片
- 3.2 一致性Hash分片
- 3.3 Range分片
- 4、分库分表的挑战
- 4.1 分布式id
- 4.2 分布式事务
- 4.3 动态扩容
- 4.4 跨库关联问题
- 4.5 服务、数据平滑迁移
- 4.6 数据库基本的增删改功能
- 4.7 子表分库分表
- 5、项目实战
1、为什么要分库分表?
随着业务的不断发展,系统数据量不断增加,即便正确设置了索引,仍然无法掩盖因为数据量过大从而导致的数据库性能下降的事实。 对于单台 MySQL 服务器来说,硬件资源(如存储容量、连接数与处理能力)是有限的。当数据量过大或读写操作并发过高时,超出了单服务器的性能瓶颈,可能会对系统的稳定性产生严重隐患,甚或导致系统整体不可用。
在仅查询请求量增加的场景中,通过主从架构实现读写分离能够满足业务读多写少的需求,从而保证系统的可靠性。然而,当数据库中单表数据量增大时,业务操作的耗时会增加。有时,由于业务特点的限制,归档或删除操作无法从根本上解决问题。此外,进行大表结构变更时需要拷贝数据,若表数据量过大会导致无剩余空间进行表结构修改。
当单表数据量过大或写操作负荷过高,以至于达到存储或性能的上限时,必须通过数据切分等方式进行治理。核心思想是通过分割数据,确保单表和单机的负荷在机器性能许可的范围内。
2、切分方案有哪些?
切分方案 | 主要解决的问题 |
---|---|
只分库不分表(分库) | 数据库读/写QPS过高,数据库连接数不足 |
只分表不分库(分表) | 单表数据量过大,存储性能遇到瓶颈 |
既分库又分表(分库分表) | 以上两种问题 |
2.1 分库
分库,指的是将数据从单个数据库拆分成多个数据库的过程。拆分后的数据库往往部署在多套集群中,这也就意味着降低了单个集群的负载压力,提升整体的读写性能。
分库通常有两种方式,垂直分库和水平分库。
2.1.1 垂直分库
垂直分库是指根据业务领域划分,将不同领域的库表拆分到多个数据库中,从而分散数据存储和读取压力,降低数据层面的耦合度。
2.1.2 水平分库
水平分库通常与分表操作一起进行。当单表数据量过大,导致存储容量和读写性能出现瓶颈时,可以根据一定规则将单表拆分成多个表,并将这些表存储在不同的数据库中,从而解决存储容量和性能问题。
2.2 分表
分表,指的是将单张数据表拆分成多张表的过程,以减少单表容量,提升读写效率。如提升数据写入时索引的构建效率、减少锁范围等;对于读流量也能较好减少耗时。
分表通常有两种方式:垂直分表和水平分表。
2.2.1 垂直分表
垂直分表是指根据实际应用场景,将数据表中的部分列拆分出来,改变原有表结构。通常可以结合实际查询需求、冷热数据分离和大字段独立存储等方式进行分表。如将db库中的订单表拆分为订单基本信息表和订单详细信息表。
2.2.2 水平分表
水平分表是指当单表数据量过大且不断增长,导致存储备份缓慢和读取效率低下时,将单表按一定规则拆分成多张表。这样可以降低单表容量,提升数据查询效率。如将db库中的订单表拆分为32个分表,order_[0-31],分表还位于同一个库中。
2.3 分库分表
在处理海量数据时,单一的数据库和数据表会面临存储容量和性能瓶颈。通过同时进行分库和分表,我们可以将单表的数据水平拆分为多个表,并将这些表分散存储在多个数据库中。这样不仅能有效降低单一数据库和数据表的负载压力,还能提升数据的读写性能和系统的整体稳定性。
3、数据水平分片方法
确定了分库分表后就需要考虑如何将数据分到哪个库或哪个表中。分片是一种水平切分数据表的方式,可以将数据按特定策略切分成多个数据文件,这些文件既可以存储在单节点上,也可以存储在分布式的多个节点上,从而实现更强大的存储和计算能力。下面介绍几种常见的数据分片方法。
3.1 Hash分片
Hash分片,就是按照数据记录中指定关键字的Hash值将数据记录映射到不同的分片中。如图所示,通过一个Hash函数计算用户ID的Hash值而后取模,分配到对应的分片。模为4的原因是系统一共有四个节点,每个节点作为一个分片。
优点:这种方法实现简洁,可以保证数据非常均匀地分布到多个分片上。
缺点:直接用节点数作为模,如果系统节点数量变动,模也随之改变,数据就要重新Hash计算,从而带来大规模的数据迁移。显然,这种方式的扩展性不好。
必须找一个方法提升系统的扩展性,就是接下来要介绍的一致性Hash。
3.2 一致性Hash分片
一致性哈希是一种特殊的哈希算法,其核心思想是在一个虚拟的环上分布数据和节点,不同于传统的哈希算法,这种方法在增加或删除节点时能够最小化数据重分布。
如图所示,首先会引入虚拟节点,每个虚拟节点就是一个分片。这个案例中将分片数量设定为16。(因为分片数量决定了集群的最大规模,所以实践中它通常会远大于初始集群节点数。)
16个分片构成了整个Hash空间,数据记录都要通过Hash函数映射到这个空间,我们换一种方式画图,可以看得更清楚些。如下图所示,Hash空间构成了一个Hash环,数据按照顺时针找到最近的节点。
当我们新增一台服务器,即节点E时,受影响的数据仅仅是新服务器到其环空间中前一台服务器(即沿着逆时针方向的第一台服务器)之间数据。结合我们的示例,只有小红分享的消息从节点B被移动到节点E,其他节点的数据保持不变。此后,节点B只存储Hash值6和7的消息,节点E存储Hash值4和5的消息。
一致性哈希优点:
- 数据均衡:能够将数据均衡地分布到多个表中,避免某些表过度负载。
- 扩展性强:当需要增加新的表时,一致性哈希允许新的表平滑地加入系统,不会导致大量数据重新分布。部分表失效或移除的情况下,一致性哈希能将其数据分布到剩余的表中,减小影响范围。
3.3 Range分片
范围分片是一种根据数据值的范围将数据拆分成多个部分(片)的分片方法。每个片包含一个特定范围内的连续数据。通常,这种方法使用数据的某个关键字段来确定数据的范围,例如日期、用户ID、订单号等。
假设有一个订单表 orders,我们可以按订单日期将其拆分成不同的分片:
orders_2020
: 存储2020年的订单
orders_2021
: 存储2021年的订单
orders_2022
: 存储2022年的订单
这种方式使用偏少,先介绍到这。工作中MySQL分片最常用的还是Hash分片。
4、分库分表的挑战
4.1 分布式id
在分库分表后,我们不能再使用mysql的自增主键。因为在插入记录的时候,不同的库生成的记录的自增id会出现冲突。因此需要有一个全局的id生成器。
4.2 分布式事务
原本采用单库,通过本地事务就可以保障一致性。分库后因为涉及到了同时更新多个数据库,如何保证要么同时成功,要么同时失败。分布式事务是分库分表绕不过去的一个坎。柔性事务是目前比较主流的方案,柔性事务包括:最大努力通知型、可靠消息最终一致性方案以及TCC两阶段提交等。
4.3 动态扩容
动态扩容指的是增加分库分表的数量。例如原来的user表拆分到2个库的4张表上。现在我们希望将分库的数量变为4个,分表的数量变为8个。这种情况下一般要伴随着数据迁移。
4.4 跨库关联问题
单库下的表连接操作,在分库后需要进行改造。常见的方式有冗余存储数据、应用层拼接等;
4.5 服务、数据平滑迁移
单库单表迁移到分库分表后,为了保证平稳过渡,上层无影响,需要采用相应的策略进行服务、数据的迁移;
4.6 数据库基本的增删改功能
对于开发人员而言,虽然分库分表,但还是希望能和单库单表那样的去简单地操作数据库,包括查询与DML操作。(通常是通过数据库中间件实现,中间件的原理大体分为:数据源代理、数据库代理。具体可搜索cobar、zebra、sharding-jdbc等关键字了解其原理)
4.7 子表分库分表
在进行主表分库分表时,子表也需要进行相应的处理。需要按照相同的分片规则进行分库分表,确保数据的关联性和查询的效率。工作量会翻倍。
5、项目实战
XXXXXXXXXXXXXXXXXXX 待补充XXXXXX