美文网首页mysql工作遇上的问题
分库分表的一些方案&思路&设计知识总结整理

分库分表的一些方案&思路&设计知识总结整理

作者: 猪大圣三七 | 来源:发表于2020-04-27 15:49 被阅读0次

    前言:一旦遇到数据量比较大的时候,小伙伴应该都知道要对数据进行拆分。有垂直和水平两种。

    垂直区分比较简单,也就是本来一个数据库,数据量大之后,从业务角度进行拆分多个库。如下图,独立的拆分出订单库和用户库。

    image.png

    水平拆分的概念,是同一个业务数据量大之后,进行水平拆分。

    image.png
    上图中订单数据达到了4000万, 大家都知道Mysq单表存储量推荐是百万级。大量的数据囤积,如果不进行处理,则会造成性能拖慢。这个时候我们就要对数据进行水平拆分,把数据拆分成4张或者更多张表。当然也可以分库,再分表。把压力从数据库层级分开。

    一、谈谈分库分表方案

    分库分表中常用的方案:Hash取模方案range范围方案
    路由算法为最主要的算法,指得是把路由的Key按照指定的算法进行存放

    以下是两个方案的特点

    1.1、Hash取模方案

    image.png

    在我们设计之时,需要先预估大概这几年的订单量,如:4000W。每张表我们可以容纳1000W,我们就可以设计4张表进行存储。

    那具体路由是如何存储的呢?hash取模方案就是对指定的路由key(如:id)对分表的总数进行取模。
    上图中,id=12的订单,对4进行取模,得到0,那此订单会放到0表中。
    id=13的订单,取模得到为1,就会放到1表中。
    为什么对4取模,是因为分表总数是4。

    • 优点:订单数据可以均匀的放在4张表中,在订单操作的时候,就不会出现热点问题

    什么是热点?
    订单集中操作在一张表中,而其他表的操作却很少。
    订单有个特点就是时间属性,一般用户操作订单数据的时候。都会集中在这个时间段产生的订单。如果这段时间产生的订单都在同一个表的情况下,就会形成热点问题,此时的压力就特别大。

    • 缺点:数量量的增大,就非常有必要做数据迁移、扩容,这将非常的困难。

    例如:业绩突飞猛进,订单量超出了4000万的量,需要增加分表数。如果我们增加4个表

    image.png

    问题就来了,一旦我们增加了分表总数,取模的计数就变成8,id=12的订单按照此方案就会到4表中查询,但之前的查询是在0表中,这就导致了数据查不到,就是因为取模的基数产生了变化。

    遇到这种情况这么办?可能大家想的就是数据迁移。把之前4000W的数据,重新做一个Hash取模方案,放到新的规划分表中。有些小公司可以接受晚上停机迁移,但大公司是不允许停机做数据迁移的。这其实是个非常痛苦的事情,那有没有不需要做数据迁移的方案呢,我们看下面的方案

    1.2、range范围方案

    range范围方案就是以指定的范围进行拆分数据。

    image.png

    此方案就是把一定范围内的订单,存放到一个表中。如上图,id=12存放到0表中,id=1300万的存放到1表中。前期设计就是把表的范围设定好,通过id进行路由存放。

    • 优点:利于扩容,不需要做数据迁移。即使再增加4张表,之前的4张表也不用改变。且新增的4张表肯定大于4000万之后的范围划分。

    -缺点:会有热点问题。因为id会一直递增变大,那就是说,会有一个时间段的订单集中在一个表中,例如id=1000W~2000W,集中在这个表中的订单就会导致1表过热,压力增大,而其他的表就没有什么压力。

    总结:

    hash取模方案:没有热点问题,但扩容迁移数据痛苦
    range方案:不需要迁移数据,但有热点问题。

    那么问题来了,能否做到即不需要迁移数据,又能解决数据热点的问题呢
    其实还有一个现实需求,能否根据服务器的性能以及存储高低,适当均匀调整存储呢?

    二、方案思路

    Range方案的基础上做Hash取模

    先使用Range方案让数据落在一个范围里,这样以后id不论怎么变大,以前的数据都不需要迁移。
    而后进行数据均分,那么是不是可以在一定范围内进行数据均分
    未来需要扩容之时,就可以提前规划好扩容的范围大小,保证范围内的数据均分。
    实现即不需要迁移数据,又能解决数据热点的问题

    三、方案设计

    image.png

    由上图可知,我们定义了一个Group组概念,将DB0、DB1、DB2三个数据库纳入Group01组。
    DB0~DB2中,也根据range范围方案取好了表与表之间id存放的范围。
    当id=0~4000万,数据落入group01组。

    关键问题
    1. gruop01有3个DB,我们如何精准分配路由id到哪个DB?
    2. 根据hash取模定位DB,那模数为多少?这里的模数必须取所有此group组DB中的表数。上图总表数为10。为什么要取表的总数?而不是DB总数3呢?
    3. 如id=12,id%10=2;那值为2,落到哪个DB库呢?这是前期设定好的,那怎么设定的呢?
    4. 一旦设计定位哪个DB后,就需要确定落到DB中的哪张表呢?

    四、核心主流程

    image.png

    根据上图的流程,我们可以有效的避免热点问题

    可以来看一下,id在【0,1000w】范围内的,根据上面的流程设计。
    1000万以内的id都均匀的分配到DB0、DB1、DB2三个数据表中的table_0中,就是用到了Hash取模方案,对10进行取模。

    那为什么对表的总数10取模,而不是DB的总数3进行取模?我们看一下为什么DB_0是4张表,其他两个DB_1是3张表?
    在我们安排服务器时,有些服务器的性能存储比较高,就可以安排多存放些数据,有些性能低的就少放点数据。如果我们取模是按照DB总数3,进行取模,那就代表着【0,4000万】的数据是平均分配到3个DB中的,那就不能够实现按照服务器能力适当分配了。

    而按照Table总数10就能够达到,如下图:


    image.png

    上图中我们对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。

    image.png

    因为是新增的一个group02组,所以就没有什么数据迁移概念,完全是新增的group组,而且这个group组照样就防止了热点,也就是【4000万,5500万】的数据,都均匀分配到三个DB的table_0表中,【5500万~7000万】数据均匀分配到table_1表中等。

    六、系统设计

    思路确定了,设计是比较简单的,就3张表,把group,DB,table之间建立好关联关系就行。

    image.png
    group和DB的关系
    image.png
    table和db的关系

    上面的表关联其实是比较简单的,只要原理思路理顺了,就ok了。小伙伴们在开发的时候不要每次都去查询三张关联表,可以保存到缓存中(Redis缓存),这样不会影响性能。

    纯学习记录,望能够帮助大家。大佬勿喷~

    相关文章

      网友评论

        本文标题:分库分表的一些方案&思路&设计知识总结整理

        本文链接:https://www.haomeiwen.com/subject/ubidwhtx.html