分库分表?如何作到永不迁移数据和避免热点? (转)

1、前言mysql


 中大型项目中,一旦遇到数据量比较大,小伙伴应该都知道就应该对数据进行拆分了。有垂直和水平两种算法

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

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

 

  上图中订单数据达到了4000万,咱们也知道mysql单表存储量推荐是百万级,若是不进行处理,mysql单表数据太大,会致使性能变慢。使用方案能够参考数据进行水平拆分。把4000万数据拆分4张表或者更多。固然也能够分库,再分表;把压力从数据库层级分开。数据库

 

2、分库分表方案 缓存


分库分表方案中有经常使用的方案,hash取模和range范围方案;分库分表方案最主要就是路由算法,把路由的key按照指定的算法进行路由存放。下边来介绍一下两个方案的特色。

一、hash取模方案

 

  在咱们设计系统以前,能够先预估一下大概这几年的订单量,如:4000万。每张表咱们能够容纳1000万,也咱们能够设计4张表进行存储。服务器

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

  • 优势:分布式

  订单数据能够均匀的放到那4张表中,这样此订单进行操做时,就不会有热点问题。函数

热点的含义:热点的意思就是对订单进行操做集中到1个表中,其余表的操做不多。

订单有个特色就是时间属性,通常用户操做订单数据,都会集中到这段时间产生的订单。若是这段时间产生的订单 都在同一张订单表中,那就会造成热点,那张表的压力会比较大。

  • 缺点:

  未来的数据迁移和扩容,会很难。

  如:业务发展很好,订单量很大,超出了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关联关系,那应用服务须要从新启动吗?

  简单点的话,就凌晨配置,重启应用服务就好了。但若是是大型公司,是不容许的,由于凌晨也有订单的。那怎么办呢?本地jvm缓存怎么更新呢?

  其实方案也不少,可使用用zookeeper,也可使用分布式配置,这里是比较推荐使用分布式配置中心的,能够将这些数据配置到分布式配置中心去

  到此为止,总体的方案介绍结束,但愿对小伙伴们有所帮助。谢谢!!!

这边隐含了一个关键点,那就是路由key(如:id)的值是很是关键的,要求必定是有序的,自增的,这个就涉及到分布式惟一id的方案.

 

 

出处:https://mp.weixin.qq.com/s/pGcDXG6kS3U86HZz7K5fgg

 

简略版分析分库分表:

分库分表,作到永不迁移数据和避免热点的方法:
    基础:
      1、数据拆分方式:垂直拆分,水平拆分。
      2、垂直拆分:原来就一个数据库,数据量一大了,就拆分为多个数据库。
      3、水平拆分:原来是t_order表,拆分红t_order_一、t_order_二、t_order_三、t_order_4。
      4、Mysql单表存储推荐是百万级,尽可能别到千万级。
    分库分表方案:
      1、经常使用的方案:hash取模、range范围方案,分库分表方案最主要的就是路由算法,把路由的key按照指定的算法进行路由存放。        
      2、hash取模方案(无热点问题,扩容困难)
          (一)、首先预估一下总数据量m,设定每张表的最大数据量n,m/n=z 是表个数。
          (二)、hash方案就是对指定的路由key(如:id)对z进行取模,"t_order_"+id%z 是表名字。
          (三)、优缺点:优势是数据均匀的分布在每张表中,不会有热点问题;缺点是数据的迁移或者扩容会很麻烦。
      3、range范围方案(不须要迁移数据,有热点问题)
          (一)、range方案是以范围进行数据拆分:id=50在0~1000的在t_order_0表、id=1050在1000~2000的t_order_1表等。
          (二)、优缺点:优势是扩容方便,不须要数据迁移;缺点是id在0-1000时,t_order_0会很忙,t_order_1,t_order_2...t_order_n...都没有数据的访问,一段时间只有一个热点表。
      4、range和hash组合方案
          (一)、设计是比较简单的,就三张表,把group,db,table之间创建好关联关系。
          group表字段:group_id,group_name,start_id,end_id
          db表字段:db_id,db_name,group_id,hash_value
          group和db的关系表字段:table_id,table_name,db_id,start_id,end_id
          
  分库分表就会带来各类join组合条件的分页查询问题,怎样解决分页查询问题,颇有挑战性。   
  
  1.事务问题:
    (1)、分布式事务 (2)、经过应用程序与数据库共同控制实现事务
          方案一:使用分布式事务
              优势:交由数据库管理,简单有效
              缺点:性能代价高,特别是shard(分片)愈来愈多时
          方案二:由应用程序和数据库共同控制
               原理:将一个跨多个数据库的分布式事务分拆成多个仅处
                     于单个数据库上面的小事务,并经过应用程序来总控
                     各个小事务。
               优势:性能上有优点
               缺点:须要应用程序在事务控制上作灵活设计。若是使用  
                     了spring的事务管理,改动起来会面临必定的困难。
  2.跨节点Join的问题
       只要是进行切分,跨节点Join的问题是不可避免的。可是良好的设计和切分却能够减小此类状况的发生。解决这一问题的广泛作法是分两次查询实现。在第一次查询的结果集中找出关联数据的id,根据这些id发起第二次请求获得关联数据。

  3.跨节点的count,order by,group by以及聚合函数问题
  
  解决方案:与解决跨节点join问题的相似,分别在各个节点上获得结果后在应用程序端进行合并。和join不一样的是每一个结点的查询能够并行执行,所以不少时候它的速度要比单一大表快不少。但若是结果集很大,对应用程序内存的消耗是一个问题。
相关文章
相关标签/搜索