1、前言
中大型项目中,一旦遇到数据量比较大,小伙伴应该都知道就应该对数据进行拆分了。有垂直和水平两种。mysql
垂直拆分比较简单,也就是原本一个数据库,数据量大以后,从业务角度进行拆分多个库。以下图,独立的拆分出订单库和用户库。算法
水平拆分的概念,是同一个业务数据量大以后,进行水平拆分。sql
上图中订单数据达到了4000万,咱们也知道mysql单表存储量推荐是百万级,若是不进行处理,mysql单表数据太大,会致使性能变慢。使用方案能够参考数据进行水平拆分。把4000万数据拆分4张表或者更多。固然也能够分库,再分表;把压力从数据库层级分开。数据库
2、分库分表方案
分库分表方案中有经常使用的方案,hash取模和range范围方案;分库分表方案最主要就是路由算法,把路由的key按照指定的算法进行路由存放。下边来介绍一下两个方案的特色。缓存
一、hash取模方案服务器
在咱们设计系统以前,能够先预估一下大概这几年的订单量,如:4000万。每张表咱们能够容纳1000万,也咱们能够设计4张表进行存储。jvm
那具体如何路由存储的呢?hash的方案就是对指定的路由key(如:id)对分表总数进行取模,上图中,id=12的订单,对4进行取模,也就是会获得0,那此订单会放到0表中。id=13的订单,取模获得为1,就会放到1表中。为何对4取模,是由于分表总数是4。
- 优势:
订单数据能够均匀的放到那4张表中,这样此订单进行操做时,就不会有热点问题。工具
热点的含义:热点的意思就是对订单进行操做集中到1个表中,其余表的操做不多。性能
订单有个特色就是时间属性,通常用户操做订单数据,都会集中到这段时间产生的订单。若是这段时间产生的订单 都在同一张订单表中,那就会造成热点,那张表的压力会比较大。spa
- 缺点:
未来的数据迁移和扩容,会很难。
如:业务发展很好,订单量很大,超出了4000万的量,那咱们就须要增长分表数。若是咱们增长4个表
一旦咱们增长了分表的总数,取模的基数就会变成8,之前id=12的订单按照此方案就会到4表中查询,但以前的此订单时在0表的,这样就致使了数据查不到。就是由于取模的基数产生了变化。
遇到这个状况,咱们小伙伴想到的方案就是作数据迁移,把以前的4000万数据,从新作一个hash方案,放到新的规划分表中。也就是咱们要作数据迁移。这个是很痛苦的事情。有些小公司能够接受晚上停机迁移,但大公司是不容许停机作数据迁移的。
固然作数据迁移能够结合本身的公司的业务,作一个工具进行,不过也带来了不少工做量,每次扩容都要作数据迁移
那有没有不须要作数据迁移的方案呢,咱们看下面的方案
二、range范围方案
range方案也就是以范围进行拆分数据。
range方案比较简单,就是把必定范围内的订单,存放到一个表中;如上图id=12放到0表中,id=1300万的放到1表中。设计这个方案时就是前期把表的范围设计好。经过id进行路由存放。
- 优势
咱们小伙伴们想一下,此方案是否是有利于未来的扩容,不须要作数据迁移。即时再增长4张表,以前的4张表的范围不须要改变,id=12的仍是在0表,id=1300万的仍是在1表,新增的4张表他们的范围确定是 大于 4000万以后的范围划分的。
- 缺点
有热点问题,咱们想一下,由于id的值会一直递增变大,那这段时间的订单是否是会一直在某一张表中,如id=1000万 ~ id=2000万之间,这段时间产生的订单是否是都会集中到此张表中,这个就致使1表过热,压力过大,而其余的表没有什么压力。
三、总结:
hash取模方案:没有热点问题,但扩容迁移数据痛苦
range方案:不须要迁移数据,但有热点问题。
那有什么方案能够作到二者的优势结合呢?,即不须要迁移数据,又能解决数据热点的问题呢?
其实还有一个现实需求,可否根据服务器的性能以及存储高低,适当均匀调整存储呢?
3、方案思路
hash是能够解决数据均匀的问题,range能够解决数据迁移问题,那咱们能够不能够二者相结合呢?利用这二者的特性呢?
咱们考虑一下数据的扩容表明着,路由key(如id)的值变大了,这个是必定的,那咱们先保证数据变大的时候,首先用range方案让数据落地到一个范围里面。这样之后id再变大,那之前的数据是不须要迁移的。
但又要考虑到数据均匀,那是否是能够在必定的范围内数据均匀的呢?由于咱们每次的扩容确定会事先设计好此次扩容的范围大小,咱们只要保证此次的范围内的数据均匀是否是就ok了。
4、方案设计
咱们先定义一个group组概念,这组里面包含了一些分库以及分表,以下图
上图有几个关键点:
1)id=0~4000万确定落到group01组中
2)group01组有3个DB,那一个id如何路由到哪一个DB?
3)根据hash取模定位DB,那模数为多少?模数要为全部此group组DB中的表数,上图总表数为10。为何要去表的总数?而不是DB总数3呢?
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表中,为何能够均匀,由于咱们用了hash的方案,对10进行取模。
上面咱们也提了疑问,为何对表的总数10取模,而不是DB的总数3进行取模?咱们看一下为何DB_0是4张表,其余两个DB_1是3张表?
在咱们安排服务器时,有些服务器的性能高,存储高,就能够安排多存放些数据,有些性能低的就少放点数据。若是咱们取模是按照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万】的数据量。
注意:小伙伴千万不要被DB_1或DB_2中table的范围也是0~4000万疑惑了,这个是范围区间,也就是id在哪些范围内,落地到哪一个表而已。
上面一大段的介绍,就解决了热点的问题,以及能够按照服务器指标,设计数据量的分配。
6、如何扩容
其实上面设计思路理解了,扩容就已经出来了;那就是扩容的时候再设计一个group02组,定义好此group的数据范围就ok了。
由于是新增的一个group01组,因此就没有什么数据迁移概念,彻底是新增的group组,并且这个group组照样就防止了热点,也就是【4000万,5500万】的数据,都均匀分配到三个DB的table_0表中,【5500万~7000万】数据均匀分配到table_1表中。
7、系统设计
思路肯定了,设计是比较简单的,就3张表,把group,DB,table之间创建好关联关系就好了。
group和DB的关系
table和db的关系
上面的表关联实际上是比较简单的,只要原理思路理顺了,就ok了。小伙伴们在开发的时候不要每次都去查询三张关联表,能够保存到缓存中(本地jvm缓存),这样不会影响性能。
一旦须要扩容,小伙伴是否是要增长一下group02关联关系,那应用服务须要从新启动吗?
简单点的话,就凌晨配置,重启应用服务就好了。但若是是大型公司,是不容许的,由于凌晨也有订单的。那怎么办呢?本地缓存怎么更新呢?
到此为止,总体的方案介绍结束,但愿对小伙伴们有所帮助。谢谢!!!