在中大型项目中,一旦数据量比较大,大家应该都知道应该对数据进行拆分。有立式和卧式两种。图片来自Pexels垂直拆分比较简单,就是一个数据库在数据量大之后,原本从业务角度拆分成多个库。如下图,订单库和用户库独立拆分:水平拆分的概念是在同一业务数据量大后进行水平拆分。上图中的订单数据已经达到了4000万。我们也知道MySQL单表的推荐存储容量是百万。如果不处理,MySQL单表的数据太大,会降低性能。使用方案可以参考数据进行水平拆分。将4000万条数据拆分为4个或更多表。当然也可以先分库再分表;将压力与数据库级别分开。分库分表方案分库分表方案中有常用的方案,如hashmodulo和rangerange方案;分库分表方案最重要的部分是路由算法,路由key按照指定的算法进行路由和存储。下面分别介绍这两种方案的特点。①哈希取模方案在我们设计系统之前,我们可以估算一下过去几年的订单量,比如:4000万。每张表可以容纳1000万,或者我们可以设计4张表来存储。具体如何路由和存储?hash解决方案是将指定的routingkey(如:id)对分表总数取模。上图中:对于id=12的订单,对4取模,即得到0,则将此订单放入0表。对于id=13的订单,如果取模值为1,则放入1表。为什么要取模4,因为分表总数是4个。优点:订单数据可以均匀的放在4个表中,这样订单操作的时候不会出现热点。热点的含义:热点是指对订单的操作集中在一张表中,其他表的操作很少。订单的一个特点是时间属性。一般用户在操作订单数据时,都会集中在这段时间产生的订单。如果这段时间产生的订单都在同一个订单表中,就会形成一个热点,那张表的压力会比较大。缺点:未来的数据迁移和扩展会比较困难。比如:业务发展很好,订单量很大,超过4000万,那我们就需要增加分表的数量。如果我们增加4张表:一旦我们增加了分表的总数,取模的基数就会变成8,按照这个方案会在4张表中查询id=12的之前的订单,但是之前的订单在0表,导致找不到数据。这是因为模数的基数发生了变化。遇到这种情况,小伙伴们想到的解决办法就是做数据迁移,把之前的4000万数据重新做一个hashscheme,放到新的规划子表中。即我们需要做数据迁移。这是一件很痛苦的事情。一些小公司可以接受晚上停机迁移,但是大公司不允许停机进行数据迁移。当然可以结合自己公司的业务做数据迁移,做一个工具,但是也带来很大的工作量,每次扩展都需要做数据迁移。有没有不需要数据迁移的解决方案?让我们看看下面的解决方案。②range范围计划范围计划就是按范围拆分数据:范围计划比较简单,就是把一定范围内的订单存储在一张表中;到1表。本方案设计时,前期设计表的范围。路由按id存储。优点:让我们小伙伴们想想这个方案是否有利于以后的扩展,不需要数据迁移。即使再增加4个表,也不需要改变前面4个表的范围。id=12的还在表0,id=1300万的还在表1,新加的4个表的范围必须大于4000万后,再分范围。缺点:有热点。我们想一想,因为id的值会一直增加,变大。这段时间的订单会一直在某个表吗?比如id=1000万到id=2000万之间,会不会把这段时间产生的订单全部集中到这张表里,这样就会导致1号表过热,压力太大,而其他表没有压力。总结:哈希取模方案:不存在热点问题,但扩容和迁移数据很痛苦。范围方案:不需要迁移数据,但有热点。那么有没有什么方案可以结合两者的优点呢?即不需要迁移数据,也能解决数据热点问题?其实还有一个实际的需要,是否根据服务器的性能和存储水平来适当、均匀地调整存储呢?解决思路是hash可以解决数据统一性问题,range可以解决数据迁移问题。我们可以将两者结合起来吗?如何利用两者的特点?我们考虑一下,数据的膨胀意味着路由key(比如id)的值变大了,这个是肯定的,那么我们首先保证,当数据变大的时候,我们先使用range方案,让数据落入一个范围。这样以后如果id变大了,之前的数据就不用迁移了。但我们也需要考虑数据的统一性。数据能否在一定范围内统一?因为我们每次扩容的时候,肯定会提前设计好这个扩容的范围。我们只需要保证这个范围内的数据就可以了。对于方案设计,我们首先定义一个分组分组概念,分组概念包括一些分库分表,如下图:上图中有几个关键点:id=0~4000万必须落入组01组。group01组里有3个DB,怎么把id路由到哪个DB?根据哈希模数定位DB,什么是模数?模数应该是所有组数据库中的表数。上图中的表总数为10。为什么要去总表数?而不是DB3的总数?比如id=12,id%10=2;值为2,属于哪个DB?这是前期定下的设计,那么怎么定呢?一旦设计定位到哪个DB,就要确定哪个表落入DB?核心主流程就是上面的流程,我们可以根据这个规则定位到一个id。让我们看看是否可以避免热点问题。我们看一下,如果id在[0,1000万]范围内,按照上面的流程设计,1000万以内的id均匀分布到DB_0、DB_1、DB_2三个数据库的Table_0表中。为什么它们可以均匀分布?因为我们使用hashscheme取模10。上面我们也提出了疑问,为什么我们取表总数取模10,而不是DB总数取3?我们看看为什么DB_0有4张表,另外两个DB_1有3张表?我们在安排服务器的时候,有的服务器性能高,存储容量大,可以安排多存储数据,有的服务器性能低,可以少存储数据。如果我们按照3个DB的总数取模,就意味着[0,4000万]的数据平均分布在3个DB中,不能按照服务器容量合理分布。可以根据表10的总数来实现,看如何实现。上图中,我们取模10,如果值为[0,1,2,3],则路由到DB_0,[4,5,6]路由到DB_1,而[7,8,9]将被路由到DB_2。现在大家明白了吗,这个设计可以把DB_0的数据多放一点,其他两个DB的数据可以少一点。DB_0承载了4/10的数据量,DB_1承载了3/10的数据量,DB_2也承载了3/10的数据量。整个Group01承载了[0,4000万]条数据量。注意:小伙伴们不要被DB_1或者DB_2中表的范围搞糊涂了,同样是0到4000万。这个就是范围区间,也就是这个id在哪个范围,就登陆到哪个表。上一节的介绍解决了热点问题,可以根据服务器指标来设计数据量的分配。如何扩容其实上面的设计思路都明白,扩容已经出来了;也就是在扩容的时候,设计一个group02组,定义好这个组的数据范围,就可以了。因为是新加入的group01,所以没有数据迁移的概念。完全是新加的群,这个群还是防热点的。即[4000万,5500万]的数据均匀分布到三个DB的table_0,[5500万到7000万]的数据均匀分布到table_1。系统设计思路确定,设计比较简单。只需要在三个表中建立group、db、table的关系即可。group与DB的关系表与db的关系上面的表关联其实比较简单,只要把原理和思路理顺了就ok了。小伙伴们,开发的时候不要每次都去查询这三个关联表,可以保存在缓存中(本地JVM缓存),不会影响性能。一旦需要扩容,是否要添加group02关联,然后需要重启应用服务?简单点,凌晨配置一下,重启应用服务即可。但是如果是大公司就不行了,因为凌晨还有订单。所以我该怎么做?如何更新本地JVM缓存?其实解决办法有很多。您可以使用Zookeeper或分布式配置。这里推荐使用分布式配置中心。您可以将这些数据配置到分布式配置中心。去。至此,整体的程序介绍就结束了,希望对小伙伴们有所帮助。谢谢你!!!PS:这里隐含了一个关键点,就是路由key(如:id)的取值非常关键,要求必须有序自增。这涉及分布式唯一标识方案。作者:老顾聊科技编辑:陶佳龙征稿:有意投稿或求报道,请加小编微信gordonlonglong
