1。前言在大型项目中,一旦数据量比较大,大家应该都知道要对数据进行拆分。有立式和卧式两种。垂直拆分比较简单,就是一个数据库在数据量大之后,本来从业务角度拆分成多个数据库。如下图,订单库和用户库是独立拆分的。水平拆分的概念是在同一个业务有大量数据后进行水平拆分。上图中,订单数据已经达到了4000万。我们也知道,建议mysql单表的存储容量最好。如果不处理,mysql单表的数据太大,会拖慢性能。使用方案可以参考数据进行水平拆分。将4000万条数据拆分为4个或更多表。当然也可以先分库再分表;将压力与数据库级别分开。2、分库分表方案分库分表方案中有常用的方案,如hash取模和range范围方案;分库分表方案最重要的部分是路由算法,路由key按照指定的算法进行路由和存储。下面分别介绍这两种方案的特点。1.哈希取模方案在我们设计系统之前,我们可以预估过去几年的订单量,比如: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,放到新的规划子表中。即我们需要做数据迁移。这是一件很痛苦的事情。一些小公司可以接受晚上停机迁移,但是大公司不允许停机进行数据迁移。当然可以结合自己公司的业务做数据迁移,做一个工具,但是也带来很大的工作量,每次扩展都需要做数据迁移。那么有没有不需要数据迁移的方案呢?下面看一下解决方案:2.范围方案范围方案是按范围拆分数据。范围方案比较简单,就是将一定范围内的订单存储在一张表中;如上图,id=12放在0表,id=1300万放在1表。本方案设计时,前期设计表的范围。路由按id存储。优点让我们小伙伴们想一想,这个方案是不是有利于以后的扩展,不需要数据迁移。立即添加4个表。前面4个表的范围不需要改变。id=12的还在表0,id=1300万的还在表1,新加的4个表的范围必须大于4000万后,再分范围。缺点是热点问题。我们想一想,因为id的值会一直增加,变大。这段时间的订单会不会在某个表里,比如id=1000万到id=2000万,这段时间会不会所有产生的订单都集中在这个表里,会导致表过热压力过大1、而其他表没有压力。3、总结:hash取模方案:无热点问题,但数据迁移扩容痛苦。范围方案:不需要迁移数据,但有热点。那么有没有什么方案可以结合两者的优点呢?即不需要迁移数据,也能解决数据热点问题?其实还有一个实际的需要,是否可以根据服务器的性能和存储级别适当、均匀地调整存储呢?3.解决思路:hash可以解决数据统一性问题,range可以解决数据迁移问题。我们可以将两者结合起来吗?如何利用两者的特点?我们考虑一下数据扩容是什么意思,routingkey(比如id)的值变大了,这个是一定的,那我们先保证,当数据变大的时候,我们先使用范围方案,让数据落在一个范围内.这样以后id变大了,以前的数据就不用迁移了。但我们也需要考虑数据的统一性。数据能否在一定范围内统一?因为我们每次扩容的时候,肯定会提前设计好这个扩容的范围。我们只需要保证这个范围内的数据就可以了。4.方案设计先定义一个分组概念,分组概念包括一些分库分表,如下图:上图中有几个关键点:1)id=0~4000万必须落入group01组。2)group01组有3个DB,如何将ID路由到哪个DB?3)根据哈希模数定位DB,什么是模数?模数应该是该组group所有DB中的表数。表数是10,为什么要去表总数?而不是DB3的总数?4)如果id=12,id%10=2;值为2,属于哪个DB?这是前期定好的设计,那么怎么定呢?5)DB设计定位好后,需要确定哪个表落入DB?5.核心主流程根据上面的流程,我们可以根据这个规则定位到一个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在哪个范围,就落在了哪个表上。上节的介绍解决了热点问题,可以根据服务器指标设计数据量的分配。6、如何扩容其实上面的设计思路理解了,扩容就已经出来了;也就是扩容的时候,设计一个group02组,定义好这个组的数据范围就可以了。因为是新加入的group01,所以没有数据迁移的概念。是一个新的group组,这个group组还是防止热点的,即[4000万,5500万]数据均匀分布在三个DB的table_0表中,[5500万到7000万]数据均匀分布到table_1表。7、系统设计思路确定,设计比较简单。只需3张表就可以建立group、db、table的关系。group与DB的关系表与db的关系上面的表关联其实比较简单,只要把原理和思路理顺了就ok了。小伙伴们,开发的时候不要每次都去查询关联的三个表,可以保存在缓存中(本地jvm缓存),不会影响性能。一旦需要扩容,是否要添加group02关联,然后需要重启应用服务?简单点,凌晨配置一下,重启应用服务即可。但是如果是大公司就不行了,因为凌晨还有订单。所以我该怎么做?如何更新本地jvm缓存?其实解决办法有很多。您可以使用zookeeper或分布式配置。这里推荐使用分布式配置中心。您可以将这些数据配置到分布式配置中心。走吧,如果你对分布式配置中心还不了解,可以阅读《三种主流的微服务配置中心深度对比!你怎么看!》至此,整体方案的介绍就结束了,希望对小伙伴们有所帮助。谢谢!!!
