当前位置: 首页 > 编程日记 > 正文

一种可以避免数据迁移的分库分表scale-out扩容方式

原文地址:http://jm-blog.aliapp.com/?p=590

目前绝大多数应用采取的两种分库分表规则

  1. mod方式

  2. dayofweek系列日期方式(所有星期1的数据在一个库/表,或所有?月份的数据在一个库表)

这两种方式有个本质的特点,就是离散性加周期性。

例如以一个表的主键对3取余数的方式分库或分表:

那么随着数据量的增大,每个表或库的数据量都是各自增长。当一个表或库的数据量增长到了一个极限,要加库或加表的时候,
介于这种分库分表算法的离散性,必需要做数据迁移才能完成。例如从3个扩展到5个的时候:

需要将原先以mod3分类的数据,重新以mod5分类,不可避免的带来数据迁移。每个表的数据都要被重新分配到多个新的表
相似的例子比如从dayofweek分的7个库/表,要扩张为以dayofmonth分的31张库/表,同样需要进行数据迁移。

数据迁移带来的问题是

  1. 业务至少要两次发布

  2. 要专门写工具来导数据。由于各业务之间的差别,很难做出统一的工具。目前几乎都是每个业务写一套

  3. 要解决增量、全量、时间点,数据不一致等问题

如何在数据量扩张到现有库表极限,加库加表时避免数据迁移呢?
通常的数据增长往往是随着时间的推移增长的。随着业务的开展,时间的推移,数据量不断增加。(不随着时间增长的情况,
例如某天突然需要从另一个系统导入大量数据,这种情况完全可以由dba依据现有的分库分表规则来导入,因此不考虑这种问题。)

考虑到数据增长的特点,如果我们以代表时间增长的字段,按递增的范围分库,则可以避免数据迁移
例如,如果id是随着时间推移而增长的全局sequence,则可以以id的范围来分库:(全局sequence可以用tddl现在的方式也可以用ZooKeeper实现)
id在 0–100万在第一个库中,100-200万在第二个中,200-300万在第3个中 (用M代表百万数据)

或者以时间字段为例,比如一个字段表示记录的创建时间,以此字段的时间段分库gmt_create_time in range

这样的方式下,在数据量再增加达到前几个库/表的上限时,则继续水平增加库表,原先的数据就不需要迁移了
但是这样的方式会带来一个热点问题:当前的数据量达到某个库表的范围时,所有的插入操作,都集中在这个库/表了。

所以在满足基本业务功能的前提下,分库分表方案应该尽量避免的两个问题:

1. 数据迁移
2. 热点

如何既能避免数据迁移又能避免插入更新的热点问题呢?
结合离散分库/分表和连续分库/分表的优点,如果一定要写热点和新数据均匀分配在每个库,同时又保证易于水平扩展,可以考虑这样的模式:

【水平扩展scale-out方案模式一】

阶段一:一个库DB0之内分4个表,id%4 :

阶段二:增加db1库,t2和t3整表搬迁到db1

阶段三:增加DB2和DB3库,t1整表搬迁到DB2,t3整表搬迁的DB3:

为了规则表达,通过内部名称映射或其他方式,我们将DB1和DB2的名称和位置互换得到下图:

dbRule: “DB” + (id % 4)
tbRule: “t”  + (id % 4)

这样3个阶段的扩展方案中,每次次扩容只需要做一次停机发布,不需要做数据迁移。停机发布中只需要做整表搬迁。
这个相对于每个表中的数据重新分配来说,不管是开发做,还是DBA做都会简单很多。

如果更进一步数据库的设计和部署上能做到每个表一个硬盘,那么扩容的过程只要把原有机器的某一块硬盘拔下来,
插入到新的机器上,就完成整表搬迁了!可以大大缩短停机时间。

具体在mysql上可以以库为表。开始一个物理机上启动4个数据库实例,每次倍增机器,直接将库搬迁到新的机器上。
这样从始至终规则都不需要变化,一直都是:

dbRule: “DB” + (id % 4)
tbRule: “t”  + (id % 4)

即逻辑上始终保持4库4表,每个表一个库。这种做法也是目前店铺线图片空间采用的做法。

上述方案有一个缺点,就是在从一个库到4个库的过程中,单表的数据量一直在增长。当单表的数据量超过一定范围时,可能会带来性能问题。比如索引的问题,历史数据清理的问题。
另外当开始预留的表个数用尽,到了4物理库每库1个表的阶段,再进行扩容的话,不可避免的要从表上下手。那么我们来考虑表内数据上限不增长的方案:

【水平扩展scale-out方案模式二】

阶段一:一个数据库,两个表,rule0 = id % 2

分库规则dbRule: “DB0″
分表规则tbRule: “t” + (id % 2)

阶段二:当单库的数据量接近1千万,单表的数据量接近500万时,进行扩容(数据量只是举例,具体扩容量要根据数据库和实际压力状况决定):
增加一个数据库DB1,将DB0.t1整表迁移到新库DB1。
每个库各增加1个表,未来10M-20M的数据mod2分别写入这2个表:t0_1,t1_1:

分库规则dbRule:

“DB” + (id % 2)

分表规则tbRule:

if(id < 1千万){

return "t"+ (id % 2);   //1千万之前的数据,仍然放在t0和t1表。t1表从DB0搬迁到DB1库

}else if(id < 2千万){

return "t"+ (id % 2) +"_1"; //1千万之后的数据,各放到两个库的两个表中: t0_1,t1_1

}else{

throw new IllegalArgumentException("id outof range[20000000]:" + id);

}

这样10M以后的新生数据会均匀分布在DB0和DB1; 插入更新和查询热点仍然能够在每个库中均匀分布。
每个库中同时有老数据和不断增长的新数据。每表的数据仍然控制在500万以下。

阶段三:当两个库的容量接近上限继续水平扩展时,进行如下操作:
新增加两个库:DB2和DB3. 以id % 4分库。余数0、1、2、3分别对应DB的下标. t0和t1不变,
将DB0.t0_1整表迁移到DB2; 将DB1.t1_1整表迁移到DB3
20M-40M的数据mod4分为4个表:t0_2,t1_2,t2_2,t3_2,分别放到4个库中:

新的分库分表规则如下:

分库规则dbRule:

if(id < 2千万){

//2千万之前的数据,4个表分别放到4个库

if(id < 1千万){

return "db"+  (id % 2);     //原t0表仍在db0, t1表仍在db1

}else{

return "db"+ ((id % 2) +2); //原t0_1表从db0搬迁到db2; t1_1表从db1搬迁到db3

}

}else if(id < 4千万){

return "db"+ (id % 4);          //超过2千万的数据,平均分到4个库

}else{

throw new IllegalArgumentException("id out of range. id:"+id);

}

分表规则tbRule:

if(id < 2千万){        //2千万之前的数据,表规则和原先完全一样,参见阶段二

if(id < 1千万){

return "t"+ (id % 2);       //1千万之前的数据,仍然放在t0和t1表

}else{

return "t"+ (id % 2) +"_1"; //1千万之后的数据,仍然放在t0_1和t1_1表

}

}else if(id < 4千万){

return "t"+ (id % 4)+"_2";      //超过2千万的数据分为4个表t0_2,t1_2,t2_2,t3_2

}else{

throw new IllegalArgumentException("id out of range. id:"+id);

}

随着时间的推移,当第一阶段的t0/t1,第二阶段的t0_1/t1_1逐渐成为历史数据,不再使用时,可以直接truncate掉整个表。省去了历史数据迁移的麻烦。

上述3个阶段的分库分表规则在TDDL2.x中已经全部支持,具体请咨询TDDL团队。

【水平扩展scale-out方案模式三】

非倍数扩展:如果从上文的阶段二到阶段三不希望一下增加两个库呢?尝试如下方案:

迁移前:

新增库为DB2,t0、t1都放在DB0,

t0_1整表迁移到DB1
t1_1整表迁移到DB2

迁移后:

这时DB0退化为旧数据的读库和更新库。新增数据的热点均匀分布在DB1和DB2
4无法整除3,因此如果从4表2库扩展到3个库,不做行级别的迁移而又保证热点均匀分布看似无法完成。

当然如果不限制每库只有两个表,也可以如下实现:

小于10M的t0和t1都放到DB0,以mod2分为两个表,原数据不变
10M-20M的,以mod2分为两个表t0_1、t1_1,原数据不变,分别搬迁到DB1,和DB2
20M以上的以mod3平均分配到3个DB库的t_0、t_2、t_3表中
这样DB1包含最老的两个表,和最新的1/3数据。DB1和DB2都分表包含次新的两个旧表t0_1、t1_1和最新的1/3数据。
新旧数据读写都可达到均匀分布。

总而言之:
两种规则映射(函数):

  1. 离散映射:如mod或dayofweek, 这种类型的映射能够很好的解决热点问题,但带来了数据迁移和历史数据问题。

  2. 连续映射;如按id或gmt_create_time的连续范围做映射。这种类型的映射可以避免数据迁移,但又带来热点问题。

离散映射和连续映射这两种相辅相成的映射规则,正好解决热点和迁移这一对相互矛盾的问题。
我们之前只运用了离散映射,引入连续映射规则后,两者结合,精心设计,
应该可以设计出满足避免热点和减少迁移之间任意权衡取舍的规则。

基于以上考量,分库分表规则的设计和配置,长远说来必须满足以下要求

    1. 可以动态推送修改

    2. 规则可以分层级叠加,旧规则可以在新规则下继续使用,新规则是旧规则在更宽尺度上的拓展,以此支持新旧规则的兼容,避免数据迁移

    3. 用mod方式时,最好选2的指数级倍分库分表,这样方便以后切割。



相关文章:

openstack对比其他有什么优点

openstack对比其他有什么优点 &#xff08;1&#xff09;模块松耦合。与其他开源软件相比&#xff0c;OpenStack模块分明。添加独立功能的组件非常简单。有时候&#xff0c;不需要通读整个OpenStack的代码&#xff0c;只需要了解其接口规范及API使用&#xff0c;就可以轻松地添…

C#和Unity编码和游戏开发学习教程

MP4 |视频:h264&#xff0c;1280720 |音频:AAC&#xff0c;44.1 KHz&#xff0c;2 Ch 语言&#xff1a;英语中英文字幕&#xff08;根据原英文字幕机译更准确&#xff09; |时长:110节课(26小时25分钟)|大小解压后:18.6 GB 含课程文件 学习C#和Unity编码和游戏开发 The Ultimat…

Linux命令行与命令

Linux命令行与命令 作者&#xff1a;Vamei 出处&#xff1a;http://www.cnblogs.com/vamei 欢迎转载&#xff0c;也请保留这段声明。谢谢&#xff01; Linux的命令是很重要的工具&#xff0c;也往往是初学者最大的瓶颈。有朋友用了很长时间的Linux图形界面&#xff0c;基本不使…

Java学习总结:58(Collections工具类)

Collections工具类 Java提供了一个集合的工具类——Collections&#xff0c;这个工具类可以实现List、Set、Map集合的操作。Collections类的常用方法如下&#xff1a; No.方法类型描述1public static < T > boolean addAll(Collection<? super T> c,T…elements…

2022-2028年中国醋酸行业投资分析及前景预测报告

【报告类型】产业研究 【报告价格】4500起 【出版时间】即时更新&#xff08;交付时间约3个工作日&#xff09; 【发布机构】智研瞻产业研究院 【报告格式】PDF版 本报告介绍了中国醋酸行业市场行业相关概述、中国醋酸行业市场行业运行环境、分析了中国醋酸行业市场行业的…

[转] linux系统文件流、文件描述符与进程间关系详解

http://blog.sina.com.cn/s/blog_67b74aea01018ycx.html linux&#xff08;unix&#xff09;进程与文件的关系错综复杂&#xff0c;本教程试图详细的阐述这个问题。包括&#xff1a;1、linux多/单进程与多/单文件对于文件流和描述符在使用时的关联情况及一些需要注意的问题。2、…

VMware虚拟机 取消 简易安装

我的VMware更新到12.0版本了&#xff0c;突然有了个简易安装操作系统&#xff0c;不得不承认这个功能很省事&#xff0c;如果没有特殊要求&#xff0c;这样安装虚拟机既快速有稳定。 但是专业人士总是要专门的服务&#xff0c;追求自动化&#xff0c;往往忽略一些细节上的东西。…

UE5蓝图初学课程 Unreal Engine 5: Blueprints for Beginners

时长:6h 51m |视频:. MP4 1280720&#xff0c;30 fps(r) |音频:AAC&#xff0c;44100 Hz&#xff0c;2ch |大小解压后:4.4 GB 语言&#xff1a;英语中英文字幕&#xff08;根据原英文字幕机译更准确&#xff09; 没有编码的游戏开发 开发一个高质量的游戏&#xff0c;不需要写…

SQL基础学习总结:1(数据库)

数据库 我们通常将可以将大量数据保存下来&#xff0c;通过计算机加工而成的可以进行高效访问的数据集合称为数据库&#xff0c;简称&#xff1a;DB。 而用来管理数据库的计算机系统则称之为数据库管理系统&#xff0c;简称&#xff1a;DBMS。一般情况下&#xff0c;系统的使用…

MyEclipse安装Freemarker插件

以下是安装Freemarker插件并设置MyEclipse使用该插件的步骤&#xff1a;1、下载Freemarker插件 打开http://sourceforge.net/projects/freemarker-ide/files/ 2、解压&#xff0c;将hudson.freemarker_ide_0.9.14文件夹放入eclipse下的plugins文件夹。 发现hudson.freemarker_…

单例模式为什么使用volatile,以及双重检查&单例模式的一些思考

也就是第一个if(singleton==null),这个是为了代码提高代码执行效率,由于单例模式只要一次创建实例即可,所以当创建了一个实例之后,再次调用getInstance方法就不必要进入同步代码块,不用竞争锁。直接返回前面创建的实例即可。

2022-2028年中国IT外包市场投资分析及前景预测报告

【报告类型】产业研究 【报告价格】4500起 【出版时间】即时更新&#xff08;交付时间约3个工作日&#xff09; 【发布机构】智研瞻产业研究院 【报告格式】PDF版 本报告介绍了中国IT外包行业市场行业相关概述、中国IT外包行业市场行业运行环境、分析了中国IT外包行业市场…

iOS,Android,WP, .NET通用AES加密算法

这两天为移动App开发API&#xff0c;结果实现加密验证时碰到一大坑。这里不得不吐槽下又臭又硬的iOS&#xff0c;Windows Server无法解密出正确的结果&#xff0c;Android则可以&#xff0c;后来使用了通用的AES256加密算法才最终搞定。 搞服务器端小伙伴没有接触过iOS&#xf…

【Linux】Linux下的硬链接与符号链接

Linux的初学者常常混淆Linux中的硬链接&#xff08;hard link&#xff09;与符号链接&#xff08;symbol link&#xff09;的概念&#xff0c;分不清楚它们的区别。因此&#xff0c;本文将向读者全面介绍硬链接与符号链接&#xff0c;给予读者以全面的认识。 1.Linux下的文件系…

Unity + SQL数据库创建管理玩家排行榜学习教程

Unity SQL Databases Player Management Leaderboards More! Unity SQL数据库玩家管理排行榜更多&#xff01; MP4 |视频:h264&#xff0c;1280720 |音频:AAC&#xff0c;44.1 KHz&#xff0c;2 Ch 语言&#xff1a;英语中英文字幕&#xff08;根据原英文字幕机译更准确&…

SQL基础学习总结:2(表的创建、删除、更新和名称修改)

表的创建、删除、更新和名称修改 登录MySQL(mysql -u root -p语句) 步骤&#xff1a; 1.首先得先使用mysql -u root -p语句登录数据库&#xff1b; 2.在"Enter password:"后面输入你设置的密码&#xff0c;然后就登录到MySQL可以进行数据库得操作了。 如下图&#…

需要抛出异常的情况

1、调用一个抛出已检查异常的方法。例如FileInputStream构造器。 2、程序运行中发现错误&#xff0c;并且利用throw语句抛出一个已检查异常。 3、程序出现错误。例如a[-1]0会抛出一个ArrayIndexOutOfBoundsException异常。 4、Java虚拟机和运行时库出现的内部错误。 对于可能会…

2022-2028年中国HDPE膜行业市场研究及投资发展分析报告

【报告类型】产业研究 【报告价格】4500起 【出版时间】即时更新&#xff08;交付时间约3个工作日&#xff09; 【发布机构】智研瞻产业研究院 【报告格式】PDF版 本报告介绍了中国HDPE膜行业市场行业相关概述、中国HDPE膜行业市场行业运行环境、分析了中国HDPE膜行业市场…

linux+postfix+extmail+dovecot搭建邮件服务器

一、我们可以重新搭建服务器&#xff0c;也可以利用我前面的搭建方法编译安装&#xff0c;地址&#xff1a;http://wangzan18.blog.51cto.com/8021085/1605480&#xff0c;本次我们使用yum的方法来安装httpd和mysql&#xff0c;我的搭建环境还是我们的CentOS6.6 x86-64位mini版…

2009年3月

1、http://www.west-wind.com/Weblog/posts/509108.aspx Client Templating with jQuery http://jtemplates.tpython.com/ jTemplates is a template engine for JavaScript. 2、http://www.cnblogs.com/QLeelulu/archive/2008/11/27/1342722.html jQuery的模板与数据绑定插件…

UE4创建第一人称射击游戏学习教程

Unreal Engine 4: Create Your Own First-Person Shooter MP4 |视频:h264&#xff0c;1280720 |音频:AAC&#xff0c;44.1 KHz&#xff0c;2 Ch 语言&#xff1a;英语中英文字幕&#xff08;根据原英文字幕机译更准确&#xff09; |时长:38节课(9h 56m) |大小解压后:6.8 GB 学…

SQL基础学习总结:3(select语句基础算术运算符比较运算符)

select语句基础 列的查询 从表中选取数据时需要使用select语句&#xff0c;通过select语句查询并选取出必要数据的过程称为匹配查询或查询。 语法结构如下&#xff1a; select <列名1>,<列名2>,<列名3>... from <表名>;该select语句包含了select和f…

Linux——Linux概念架构的理解

摘要 Linux kernel成功的两个原因&#xff1a;&#xff08;1&#xff09;灵活的架构设计使得大量的志愿开发者能够很容易加入到开发过程中&#xff1b;&#xff08;2&#xff09;每个子系统&#xff08;尤其是那些需要改进的&#xff09;都具备良好的可扩展性。正是这两个原因使…

2022-2028年中国GPS导航行业投资分析及前景预测报告

【报告类型】产业研究 【报告价格】4500起 【出版时间】即时更新&#xff08;交付时间约3个工作日&#xff09; 【发布机构】智研瞻产业研究院 【报告格式】PDF版 本报告介绍了中国GPS导航行业市场行业相关概述、中国GPS导航行业市场行业运行环境、分析了中国GPS导航行业市…

Geohash的精度问题

网上的关于Geohash的精度问题的说明都不够完整&#xff0c;做了一下补充&#xff0c;可以参见本文表格。 具体的计算方法&#xff1a;Latitude的范围是&#xff1a;-90 到 90Longitude的范围&#xff1a;-180 到 180地球参考球体的周长&#xff1a;40075016.68米geohash长度Lat…

在CentOS上把MySQL从5.5升级到5.6

在CentOS上把MySQL从5.5升级到5.6 摘要&#xff1a;本文记录了在CentOS 6.3上&#xff0c;把MySQL从5.5.28升级到5.6.19的过程。 1. 概述 在我做的一个项目中&#xff0c;最近我对生产服务器上的一系列系统软件进行了升级&#xff0c;包括Git、Nginx、MySQL和PHP。这篇文章讲的…

在3ds Max和Vray中创建赛博朋克圣诞老人室内场景

我叫奥马尔萨米罗什迪&#xff0c;建筑师、室内设计师、助理讲师、摄影师和3d艺术家。2019年获得建筑学硕士学位。我曾为多家建筑事务所和顾问工作&#xff0c;甚至建立了自己的建筑工作室。我有很长的大学教学生涯&#xff0c;从2013年开始&#xff1b;我曾在知名大学教授设计…

Java之网络通信框架mina

mina是一个基于java nio的网络通信框架。主要屏蔽了网络通信的一些细节,对Socket进行封装,并且是NIO的一个实现架构,可以帮助我们快速的开发网络通信,常用于游戏的开发、中间件服务端的程序中。Apache的Mina(Multipurpose Infrastructure Networked Applications)是一个网络应用框架,可以帮助用户开发高性能和高扩展性的网络应用程序;它提供了一个抽象的、事件驱动的异步API,使。

GIT常用命令大全

git config --global color.ui true //让git显示颜色//忽略特殊文件//.gitignore文件 # Windows: Thumbs.db*.egg*.egg-infodist//把该文件也提交到git$ git add -f App.class //强制添加被忽略的特殊文件。

SQL基础学习总结:5(HAVING子句的使用ORDER BY排序子句的使用)

为聚合结果指定条件 HAVING子句 HAVING子句可以让我们筛选分组之后的各种数据&#xff0c;其语法结构如下&#xff1a; SELECT <列名1>,<列名2>,<列名3> FROM <表名> GROUP BY <列名1>,<列名2>,<列名3> HAVING <分组结果对应的…