分库分表这样玩,可以永不迁移数据、避免热点

字母哥的博客 2019-11-19 13:07:21 ⋅ 789 阅读
作者 | 老顾聊技术链接 | www.toutiao.com/i6677459303055491597

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

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

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

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

分库分表方案

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


1、hash取模方案

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

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

  • 优点:

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

热点的含义:热点的意思就是对订单进行操作集中到1个表中,其他表的操作很少。

订单有个特点就是时间属性,一般用户操作订单数据,都会集中到这段时间产生的订单。如果这段时间产生的订单 都在同一张订单表中,那就会形成热点,那张表的压力会比较大。

  • 缺点:

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

如:业务发展很好,订单量很大,超出了4000万的量,那我们就需要增加分表数。如果我们增加4个表

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

遇到这个情况,我们小伙伴想到的方案就是做数据迁移,把之前的4000万数据,重新做一个hash方案,放到新的规划分表中。也就是我们要做数据迁移。这个是很痛苦的事情。有些小公司可以接受晚上停机迁移,但大公司是不允许停机做数据迁移的。

当然做数据迁移可以结合自己的公司的业务,做一个工具进行,不过也带来了很多工作量,每次扩容都要做数据迁移

那有没有不需要做数据迁移的方案呢,我们看下面的方案


2、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表过热,压力过大,而其他的表没有什么压力。


3、总结:

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

range方案:不需要迁移数据,但有热点问题。

那有什么方案可以做到两者的优点结合呢?,即不需要迁移数据,又能解决数据热点的问题呢?

其实还有一个现实需求,能否根据服务器的性能以及存储高低,适当均匀调整存储呢?

方案思路

hash是可以解决数据均匀的问题,range可以解决数据迁移问题,那我们可以不可以两者相结合呢?利用这两者的特性呢?

我们考虑一下数据的扩容代表着,路由key(如id)的值变大了,这个是一定的,那我们先保证数据变大的时候,首先用range方案让数据落地到一个范围里面。这样以后id再变大,那以前的数据是不需要迁移的

但又要考虑到数据均匀,那是不是可以在一定的范围内数据均匀的呢?因为我们每次的扩容肯定会事先设计好这次扩容的范围大小,我们只要保证这次的范围内的数据均匀是不是就ok了。

方案设计


我们先定义一个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中的哪张表呢?

核心主流程

按照上面的流程,我们就可以根据此规则,定位一个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在哪些范围内,落地到哪个表而已。

上面一大段的介绍,就解决了热点的问题,以及可以按照服务器指标,设计数据量的分配。

如何扩容

其实上面设计思路理解了,扩容就已经出来了;那就是扩容的时候再设计一个group02组,定义好此group的数据范围就ok了。

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

系统设计

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

group和DB的关系

table和db的关系

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

一旦需要扩容,小伙伴是不是要增加一下group02关联关系,那应用服务需要重新启动吗?

简单点的话,就凌晨配置,重启应用服务就行了。但如果是大型公司,是不允许的,因为凌晨也有订单的。那怎么办呢?本地jvm缓存怎么更新呢?

其实方案也很多,可以使用用zookeeper,也可以使用分布式配置,这里是比较推荐使用分布式配置中心的,可以将这些数据配置到分布式配置中心去。



全部评论: 0

    我有话说:

    什么情况下才需要分库

    我请教一下,我模拟测试,搞了几张大都是3千万左右的数据,带主键查询秒级响应,jmeter插入能够达到每秒6、700条左右,我业务量每秒就是300条左右插入,这个场景下是否不需要考虑分库的问题?

    为什么说作为程序员分库的必要性一定要掌握?

      互联网大厂程序员必须掌握海量数据和高并发问题处理技能,期望进入大厂的程序员一定要仔细看这篇! MySQL 分库是做什么的? 相信很多程序员对 MySQL 都比较熟悉了,目前国内

    一文看懂mycat配置--数据库的读写分离分库

    数据库的处理本身优化也是非常重要的。主从、热备、...

    「轻阅读」大众点评是如何分库的?

    原大众点评的订单单早就已经突破两百G,由于查询维度较多,即使加了两个从库,优化索引,仍然存在很多查询不理想

    线性 - 循环链

    1.引子 单链解决了从A 查找到E的过程,假设现在要求从E 查找到A,用时最短, 因为单链是单向的,只能从前往后,无法解决这个问题。因此引出了循环链。   思路图

    您应该避免的五个简单的数据库设计错误

    Anith 在他非常成功的文章 Facts and Fallacies about First Normal Form 之后,对五个常见的数据库设计错误进行了引人入胜的讨论,尽管使用它们的不幸后果

    线性 - 栈与队列

    1.栈 1.栈(stack)是限定仅在尾进行插入和删除操作的线性,(先进后出) 2.我们把允许插入和删除的一端成为栈顶(top) 另一端称为栈底(bottom),不含任何数据元素的栈称为空栈

    推荐一款前端数据源管理工具 algeb

    ALGEB 简介 这是一个比较抽象的库,一开始可能比较难理解。我写它的初衷,是创建可响应的数据请求管理。在传统数据请求中,我们只是把携带ajax代码的一堆函数放在一起,这样可以调用接口。但是这种

    数据结构

    结构,简单的理解就是关系。严格点说,结构是指各个组成部分相互搭配和排列的方式。在现实世界中,不同数据元素之间不是独立的,而是存在特定的关系,我们将这些关系成为结构。 数据结构:是相互之间存在一种

    数据量下 MyBatis PageHelper 页查询性能问题的解决办法

    前因 项目一直使用的是PageHelper实现页功能,项目前期数据量较少一直没有什么问题。随着业务扩增,数据库扩增PageHelper出现了明显的性能问题。 几十万甚至上百万的单数据查询性能缓慢

    「转发」数据库读写分离这个坑,你应该踩过吧?

    公司第一次发布项目遇到的一个问题,一个数据库读写...

    面试官:如何设计数据库秒级平滑扩容架构?

    该方案能够实现n库扩2n库的秒级、平滑扩容,增加数据库服务能力,降低单库一半的数据量,其核心原理是:成倍扩容,避免数据迁移

    转ES6(三):数据结构

    Set 是ES6提供的一种新的数据结构,它允许你存储任何类型的唯一值,而且Set中的元素是唯一的。

    MySQL 插入 100万 条数据整理笔记

    多线程插入(单) 问:为何对同一个的插入多线程会比单线程快?同一时间对一个的写操作不应该是独占的吗? 答:在数据里做插入操作的时候,整体时间的分配这样的: 1、多链接耗时 (30

    分享】一次单体架构改造成微服务架构的拆实践

    从5个方面设计这次微服务的拆方案,以及经验总结!

    排序 --- 归并排序

    此篇文章引自 这里,个人感觉无出其右者,只好借鉴而来 归并排序 是递归分治和有序合并的简称, 首先利用分治法的思想将序列递归分裂成若干个子序列,使将子序列基本有序,最后使整体有序。 一、图示

    分库工具:Apache ShardingSphere 5.0.0-alpha 发布

    Apache ShardingSphere 5.0.0 发布了 alpha 版本,自上个版本 4.1.1 发布以来,Apache ShardingSphere 一直在修复社区反馈的问题、加强功能和开发新特性。 根据官方的说法,5.x 是 Apac...

    Dgraph 1.2.8 发布,事务性分布式图形数据库

    Dgraph 1.2.8 发布了。Dgraph 是一个可扩展的,分布式的,低延迟的图数据库,目标是提供 Google 生产水平的规模和吞吐量,在超过 TB 的结构数据里,为用户提供足够低延迟的实时

    转ES6(五):Iterator、Generator、async/await

    1. Iterator 和 for…of 循环 ES6 中有四种数据集合:数组( Array )、对象( Object )、Map 和 Set 。这样就需要一种统一的接口机制,来处理所有不同的数据