一、前言 中大型项目中,一旦遇到数据量比较大,伙伴应该都知道就应该对数据进行拆分了。有垂直和水平两种。 垂直拆分比较简单,也就是本来一个数据库,数据量大之后,从业务角度进行拆分多。..

一、前言
在中大型项目中,数据量大了之后,肯定要考虑数据拆分。主要有垂直和水平两种拆分方式。
1 | 垂直拆分比较简单,就是按业务把数据库拆开。比如原来一个库,数据量大之后,从业务角度拆分成多个库。如下图,把订单库和用户库独立出来。 |
上图中订单数据达到了4000万,MySQL单表推荐存储量是百万级,如果不处理,单表数据太大性能会变慢。这时候就需要做水平拆分,把4000万数据拆成4张表或者更多。当然也可以分库再分表,把压力从数据库层级分开。
二、分库分表方案
分库分表常用的方案有hash取模和range范围两种,核心就是路由算法,把路由的key按照指定算法进行路由存放。下面介绍下这两个方案的特点。
1、hash取模方案
设计系统之前,可以先预估下这几年的订单量,比如4000万。每张表可以容纳1000万,那就设计4张表来存储。
优点:
订单数据可以均匀分布到4张表中,这样操作时就不会有热点问题。
缺点:
将来的数据迁移和扩容会很痛苦。
1 | 比如业务发展很好,订单量超出了4000万,就需要增加分表数。如果增加4个表: |
遇到这种情况,通常想到的方案就是做数据迁移,把之前的4000万数据重新做hash,放到新的规划分表中。这个很痛苦。小公司可能可以接受晚上停机迁移,但大公司是不允许停机做数据迁移的。
那有没有不需要做数据迁移的方案呢?看下面的方案。
2、range范围方案
1 | range方案就是按范围拆分数据。 |
优点
1 | 这个方案有利于将来的扩容,不需要做数据迁移。即使再增加4张表,之前的4张表的范围不需要改变,id=12的还是在0表,id=1300万的还是在1表,新增的4张表的范围肯定是大于4000万之后的范围划分的。 |
缺点
1 | 有热点问题。因为id值会一直递增变大,这段时间的订单会一直在某一张表中,比如id=1000万~id=2000万之间,这段时间产生的订单都会集中到1表中,导致1表过热,压力过大,而其他表没什么压力。 |
3、总结:
hash取模方案:没有热点问题,但扩容迁移数据痛苦
range方案:不需要迁移数据,但有热点问题。
那有没有方案可以结合两者的优点呢?既不需要迁移数据,又能解决数据热点问题?
另外还有个现实需求:能否根据服务器的性能和存储能力,适当调整存储分布呢?
三、方案思路
hash可以解决数据均匀的问题,range可以解决数据迁移问题,那能不能两者相结合呢?利用这两者的特性?
考虑一下数据的扩容,路由key(如id)的值变大了,这个是一定的,那先保证数据变大的时候,首先用range方案让数据落地到一个范围里面。这样以后id再变大,以前的数据就不需要迁移了。
但又要考虑到数据均匀,那是不是可以在一定范围内数据均匀呢?因为每次的扩容肯定会事先设计好这次扩容的范围大小,只要保证这次范围内的数据均匀是不是就ok了。
四、方案设计
先定义一个group组概念,这组里面包含了一些分库以及分表,如下图
上图有几个关键点:
五、核心主流程
按照上面的流程,就可以根据此规则,定位一个id,看看有没有避免热点问题。
看一下,id在【0,1000万】范围内的,根据上面的流程设计,1000万以内的id都均匀的分配到DB_0,DB_1,DB_2三个数据库中的Table_0表中,为什么可以均匀,因为用了hash的方案,对10进行取模。
在安排服务器时,有些服务器的性能高,存储高,就可以安排多存放些数据,有些性能低的就少放点数据。如果取模是按照DB总数3,进行取模,那就代表着【0,4000万】的数据是平均分配到3个DB中的,那就不能够实现按照服务器能力适当分配了。
按照Table总数10就能够达到,看如何达到
上图中对10进行取模,如果值为【0,1,2,3】就路由到DB_0,【4,5,6】路由到DB_1,【7,8,9】路由到DB_2。这样的设计就可以把多一点的数据放到DB_0中,其他2个DB数据量就可以少一点。DB_0承担了4/10的数据量,DB_1承担了3/10的数据量,DB_2也承担了3/10的数据量。整个Group01承担了【0,4000万】的数据量。
上面一大段的介绍,就解决了热点的问题,以及可以按照服务器指标,设计数据量的分配。
六、如何扩容
其实上面设计思路理解了,扩容就已经出来了;那就是扩容的时候再设计一个group02组,定义好此group的数据范围就ok了。
因为是新增的一个group01组,所以就没有什么数据迁移概念,完全是新增的group组,而且这个group组照样就防止了热点,也就是【4000万,5500万】的数据,都均匀分配到三个DB的table_0表中,【5500万~7000万】数据均匀分配到table_1表中。
七、系统设计
思路确定了,设计是比较简单的,就3张表,把group,DB,table之间建立好关联关系就行了。
group和DB的关系
table和db的关系
上面的表关联其实是比较简单的,只要原理思路理顺了,就ok了。小伙伴们在开发的时候不要每次都去查询三张关联表,可以保存到缓存中(本地jvm缓存),这样不会影响性能。
一旦需要扩容,小伙伴是不是要增加一下group02关联关系,那应用服务需要重新启动吗?
简单点的话,就凌晨配置,重启应用服务就行了。但如果是大型公司,是不允许的,因为凌晨也有订单的。那怎么办呢?本地缓存怎么更新呢?
到此为止,整体的方案介绍结束,希望对小伙伴们有所帮助。谢谢!!!
本文标题: 分库分表如何做到永不迁移数据和避免热点
发布时间: 2021年01月31日 00:00
最后更新: 2025年12月30日 08:54
原始链接: https://haoxiang.eu.org/f97be36c/
版权声明: 本文著作权归作者所有,均采用CC BY-NC-SA 4.0许可协议,转载请注明出处!

