#####起因

​ 为什么要写这篇文章?最近在跟着某个大佬做项目,由于这是一个模拟高并发海量数据的项目,因此在学习的过程中会经常使用分表操作。借此机会,想要系统学习一下关于分库分表的知识,用此文章记录自己的学习过程。

#####问题:为什么要分库分表?[使用场景]

​ 在学习新知识之前,我们首先要弄清楚它的作用,这样当我们遇到类似的问题时就可以使用对应的解决方案。

首先我们需要清楚的是我们通常所说的分库分表其实包含了三种操作:只分库不分表、只分表不分库以及分库又分表。

只分库不分表(又称分库)

​ 分库操作主要解决的是并发量大的问题,由于单个数据库的连接数是有上限的(MySQL5.5、MySQL5.6和MySQL5.7的最大连接数上限100000,但默认的最大连接数都是151),虽然这个上限可以手动调控,但当并发量不断增大时,可能需要反复调控。一种更加方便的做法就是进行分库操作,分库就是将一个数据库中的数据分散到多个数据库中,不仅减少了单个数据库中的数据量,同时增加了系统的并发量(数据库增多)。

只分表不分库(又称分表)

​ 分表操作主要解决的是单张表数据量过大的问题,通过分表将单张表中的数据分散到多张表中,减少单表的数据量,提升查询性能。

分库又分表(又称分库分表)

​ 当系统的并发量以及数据量都非常大的时候,⼀般来说,单表的行数如果超过了 500 万或者单表容量超过 2 GB 之后就需要考虑终极方案——分库分表了,顾名思义分库分表就是以上两者的结合。

​ 分库分表的本质就是用空间换时间

#####如何分库分表?

​ 在了解了分库分表的使用场景后,接下来就要进入它的使用方法,这里着重介绍分库和分表,因为所谓分库分表就是分库和分表的组合。

分库

​ 根据划分的条件和维度不同可以分为水平拆分和垂直拆分

垂直拆分

​ 一般是根据业务的不同将不同业务下的表放入不同的数据库,比如商城的整个业务中的 用户相关表,订单相关表,物流相关表,商品相关表各自独立分类形成 用户系统数据库,订单系统数据库,物流系统数据库

image-20240325220600013

在需要进行分库的情况下,优先考虑垂直分库

水平分库

​ 水平分库一般是在垂直分库的基础上进一步细分,比如对用户数据库可以按照用户所在的地区、用户的注册时间、用户id等进一步划分

image-20240326113938286

分表

​ 同样分表也有两种方式:水平分表和垂直分表

水平分表

​ 每张表的字段相同,将数据分散在多张表中,减少的是单张表的行数。

image-20240326115055431

垂直分表

​ 纵向地把表中的列分成多个表,把表由宽变窄。一般遵循以下几个点进行拆分

  • 冷热分离:将常用列和不常用列放在不同的表
  • 将某些大字段列独立存放
  • 关联紧密的列放在同一个表

比如:可以将用户表中的个人信息这个大字段且不常用的字段独立放在一个表中

image-20240326114601118

分表字段和分表算法

​ 为了保证在分表后,每条数据都会插入到对应的表中,且这个操作是可重复可再现的,也就是说同一条记录在写和读时都是操作相同的表,需要指定分表字段和分表算法。

​ 数据读写的过程相当于一个哈希过程,分表字段为key,分表算法为哈希算法,各个表为对应的value。

image-20240326123528037

在分库之后,数据读写的相关细节感兴趣的可以自行搜索(可能我后面会补充,用到再说)

#####分库分表中需要注意的问题

​ 关于分库分表中的一些问题也是值得我们关注的,只有这样在面对问题时,我们才能选择出最佳方案。

数据倾斜问题

​ 所谓数据倾斜指的是由于分表字段以及分表算法的选择不当,导致有的表数据量非常大,而有的表数据量反而非常小的情况。

image-20240326133825271

显然这样的分表结果不符合我们的预期,关于分表字段的选择需要结合具体业务和需求来决定,并且一定要慎重考虑,否则就会发生数据倾斜问题。

常见的分表算法

哈希取模

​ 将分表字段进行hash转换为一个整数后,再对表的数量取模得到要插入的表。需要注意的是Java中的hash方法得到的结果有可能是负数

一致性哈希

​ 如果需要进行二次分表,表的总数量发生变化,就需要重新计算哈希值,就需要涉及到数据迁移了。

为了解决扩容的问题,我们可以采用一致性哈希的方式来做分表。

一致性哈希可以按照常用的hash算法来将对应的key哈希到一个具有2^32次方个节点的空间中,形成成一个顺时针首尾相接的闭合的环形。所以当添加一台新的数据库服务器时,只有增加服务器的位置和逆时针方向第一台服务器之间的键会受影响。

image-20240326141335947

不支持跨库事务

​ 因此分库分表后就容易出现因为不支持事务导致的数据一致性问题

分页查询以及排序等操作都将变得十分麻烦

因此虽然分库分表可以解决高并发大数据的问题,但是不能盲目使用分库分表,建议在分库分表之前应该先考虑其他优化手段,如果没有其他优化方法再使用分库分表。比如一些常见的优化手段

  1. 数据库的基本优化:做好索引、减少多表 join、减少冗余字段

  2. 减少数据库压⼒:在数据库之前加⼀层缓存,把⼀些可以接受延迟的,以及数据库变化频率较低的内容放到本地缓存或者分布式缓存当中。

  3. 冷热数据的隔离:即数据归档,可以将⼀些更新以及不经常使⽤的数据单独隔离出来,可以放到历史表或者离线数仓当中,减少表中的数据量来提升效率

  4. 数据库分区:数据库分区之后,将数据存储在不同的表当中,尽量减少单表的数据量,提升查询性能。

  5. 分布式数据库:将数据分散到多个节点上,提升容量

问题:分区和分表有什么区别?

​ –解答:我们上面提过⼀个点,就是在数据库中,如果数据量⽐较⼤的话,优先考虑的是如何对数据进行优化,而不是进行分库分表,这两个过程有什么区别?我们现在来探究⼀下。首先我们先说⼀个点,就是分区和分表相同的点,都是按照⼀定的规则,对⼀张数据量特别大的表进行分解,使得表的数据量减少,从而提高查询效率。这样听起来你可能感觉没差多少,因为两者的区别都是把表进行拆分,那具体有什么差别吗?

主要就是分区和分表后数据的存储⽅式发⽣了变化。

这个要从 MySQL 的索引说起,在 Innodb 中(8.0之前),表存储主要依赖两个⽂件,分别是 .frm ⽂件和.ibd ⽂件。.frm⽂件⽤于存储表结构定义信息(也就是我们使用desc table得到的结构),⽽.ibd⽂件则⽤于存储表数据。

分区增加的只是.ibd文件的数量,这些.ibd文件共用同一个.frm文件;而分表不仅会增加.ibd文件并且每个.ibd文件都会有一个独立的.frm文件。简单来说就是,分区实际上还是一张表,而分表则是真正将一张表拆分为多张表。

分区结构

image-20240324122916989

分表结构

image-20240324124041738

关于分库分表的一些其他细节,大家感兴趣的可以参考以下文章

再有人问你什么是分库分表,直接把这篇文章发给他 - 掘金 (juejin.cn)

大众点评订单系统分库分表实践 - 美团技术团队 (meituan.com)

微信公众平台 (qq.com)

图文解释 读写分离、垂直拆分、水平拆分、分库分表 - 掘金 (juejin.cn)

柒夭日志:分库分表篇 - 掘金 (juejin.cn)