本文来自网易云社区html
做者:马进
mysql
跑男热播,做为兄弟团忠实粉丝,笔者也是一到周五就如打鸡血乐不思蜀。
看着银幕中一众演员搞怪搞笑的浮夸演技,也时常感慨,这样一部看似简单真情流露的真人秀,必然饱含了许许多多台前幕后工做者的辛苦汗水,若是把一部真人秀比做一个互联网产品,那么在银幕中那些大明星就比如产品开发者:他们须要敏锐地把握观众的需求和口味,与终端用户直接打交道。而灯光,道具,服装,摄影这些就比如系统开发者,他们要尽一切努力知足产品开发者提出的需求,而且暴露给他们最简洁直观的接口,就像跑男同样,把一切复杂实现包装在幕后,留给众演员一个简单开阔的舞台飙戏。
对于数据库来讲同样如此,在数据容量日益膨胀的今天,单机数据库的容量,IOPS等使用瓶颈已愈发明显,在这样的背景下,产品开发者每每面临两种选择:第一是采用mongodb,hbase一类的NewSQL系统来突破单机的限制,但这种作法在产品需求不断累加时每每弊大于利,由于任何一款NewSQL或NoSQL产品都没法支持关系型数据库那样多的特性,例如事务,电商场景下必然会用到,例如支持丰富的adhoc查询。为了可以使用关系型数据库的各种特性,又支持良好的扩展性和延展性,架构师们每每会倾向选择第二种方案:分库分表的关系型数据库,将数据分分布到不一样的关系型数据库里,以突破单个数据库的各种限制。可是使用分库分表的方案会带来另外一些问题,好比应用怎样像使用普通关系型数据库同样使用分库分表的系统?当前的分库分表方案没法知足业务需求时应该如何处理?这些问题就比如真人秀节目中演员们在表演时会遇到的各种障碍,须要咱们幕后工做者,也就是系统开发者经过中间件的方案来解决。
网易分布式数据库DDB从06年开始为网易互联网核心产品提供透明的分库分表服务,10年风雨,可谓见证了各大产品兴衰荣辱,在网易核心互联网应用中基本都能看到DDB的身影。DDB的核心价值充分体现了系统开发者做为幕后工做人员的良苦用心:应用开发者在使用DDB时,基本上像使用mysql同样简单。篇幅所限,DDB的内容没法展开讨论,由于本文的重点是另一套和DDB息息相关,一样展示了幕后工做者核心价值的中间件系统:数据迁移工具Hamal。
Hamal是一套基于MySQL binlog实现的数据迁移、数据同步以及数据分发系统。DDB的存在为咱们很好地解决了应用在使用分库分表时的透明性问题,可是却没法解决数据分布扩展后带来的数据迁移问题,这里就是Hamal大展拳脚之地了,另外,因为Hamal拥有一套能够扩展的接口,它也能够在必定程度上解决数据同步和数据分发的问题。
咱们首先来看看数据迁移,数据同步和数据分发这三种应用场景。算法
数据迁移
数据迁移的需求是咱们团队开发Hamal引擎的初衷,在DDB漫长的技术支持中,曾经遇到不少数据迁移需求,这种需求能够归为两大类:
库的扩容缩容
数据库的扩容缩容是分库分表下一种很是典型的数据迁移场景,例如在DDB最先的应用产品博客项目中,出现过20个库扩40个扩的案例。在hamal工具出现之前,应对这种扩容场景的解决方案是依赖MySQL原生的复制机制,具体过程很是复杂,由于增量数据的迁移中包含脏数据,在作切换后须要一段删除增量脏数据的时间,所以实际的切换须要进行两次。基本步骤为:
sql
以一扩二的场景为例,要扩容的原库为d1:mongodb
a) 全量复制:经过MySQLDump或者innobackup对要扩容的库d1进行全量备份和恢复,恢复出来的库为r1, r2
b) 删除全量脏数据:将r1,r2中不该该属于本身的全量数据删除,1扩2的场景下,这个过程会删掉r1,r2两个库累加起来后的冗余数据
c) 增量复制:将r1,r2分别做为d1的slave进行增量数据同步,注意同步点要以备份时的binlog位置为准
d) 增量追上切库,改写SQL:这个步骤其实涉及到三件事情,首先要等r1和r2做为slave追上d1的binlog位置,而后在DDB的中间件层将数据分布表从d1改写为r1和r2,因为数据分布表已经发生变化,新的查询和更新都会在r1,r2上进行,可是r1,r2上增量数据是“不干净的”,所以须要在下发的全部查询中增长对脏数据的过滤条件。在之前的DDB版本中,会为每一个将来可能须要迁移的表增长一个默认的bucketNo字段,每一个表有多少个bucketNo在建表之初必须指定,而且将来不能更改,DDB全部表的分区字段通过hash后会对应到bucketNo上,再由bucketNo与实际的数据库节点对应,即所谓的均衡策略。在数据分布表由d1切为r1,r2后,须要在全部查询后增长bucketNo not in(other bucketNos in d1)的条件,例如对下发给r1的查询后必须增长where bucket No not in (other bucketNos in r2)。这三个过程严丝合缝,由DDB内部完成,应用毫无感知。
e) 删除增量脏数据:在完成数据分布表切换后,DDB需啊启动一个删除增量脏数据的过程,删除的方法与第二个步骤相似。
f) 撤销SQL改写:在完成删除增量脏数据的过程后,再也不须要DDB层为全部查询增长过滤脏数据的条件,此后撤销全部查询SQL的改写。
以上6步构成了老版本DDB的数据迁移方案,这个解决方案优点是对外部依赖少,整个过程除了改写SQL外,只须要一个额外的删除脏数据的进程,全量复制和增量复制都是经过MySQL自带的方案解决,可是缺陷也是很是明确的:
数据库
须要依赖bucketNo这个字段,事实上这个字段仅仅为了数据迁移这个应用场景而生,在不少不会有数据迁移的表中初始并不会有这个字段缓存
改写下发SQL和删除增量脏数据的过程发生在数据分布表切换后,会对线上业务产生必定的性能影响数据结构
这个方案仅对1扩2,2扩3规则适用,对一些比价简单的扩容场景好比9扩10,因为在改写SQL环节实现会很麻烦,并不支持架构
整个过程复杂且慢,须要DBA对原理熟悉方能操做并发
老的数据迁移方案的实施过程较为繁琐,虽然咱们在很大程度上实现了自动化,可是一旦出问题处理起来会比较麻烦,例如在切换在引入Hamal数据迁移工具后,一切的困难迎刃而解。咱们来看看一样的数据迁移需求,H使用Hamal须要经历怎样的过程:
a) 全量复制:Hamal的全量复制方案有两种,第一种是使用MySQLDump或Mydump对原数据库进行逻辑备份,再由isql或DDB的DBI模块进行数据导入,因为全量数据经过isql或DBI导入后数据会通过数据分布表从新分布,不会产生脏数据,也就省去了以后的删除全量脏数据的过程。第二种全量复制方案是在Hamal engine内部启动一个全量复制线程定批对原库数据进行数据拷贝,这个过程经过对数据批量加锁能够与增量复制并发进行,这个过程与percona的在线修改表结构方案中的全量复制很是相似。不管是哪一种方案,因为都通过数据重分布,不会向新库引入脏数据。
b) 增量复制:Hamal的增量复制方案是使用Hamal engine模拟MySQL slave向原库拉取binlog,通过自身模块的解析生成相应的数据迁移SQL,再逐条经过DBI模块apply到新库中,这样一样能够在增量复制的同时避免引入脏数据
c) 增量追上切库:因为增量复制也不会引入脏数据,也就避免了须要删除增量脏数据和改写SQL,在发觉binlog位置追上后通过短暂的数据分布表切换便可完成数据迁移过程。
基于Hamal的DDB数据迁移方案与老方案相比,步骤节省了一半,整个过程理解起来也比较容易,最重要的是它彻底解决老方案的4点缺陷:迁移过程无需依赖bucketNo字段;由于没有改写SQL和删除增量脏数据,也不会对线上应用产生任何不利影响;因为全量和增量复制过程原理都是通过DDB的数据分布表来完成,且没有SQL改写,也就没有了对1扩2,2扩3规则的限制;整个流程理解和操做起来门槛下降不少。
另外,Hamal engine基于一套有向无环图算法实现了快速的并行复制,它的增量复制过程自己就比MySQL原生的复制过程快不少,可是使用Hamal的并行复制算法具备两个限制,一是必须基于row格式binlog,二是全部迁移表必须含有主键或惟一性索引。这两个限制是由算法原理自己决定的,原理会在下文展开。首先row格式binlog自己在使用上比Statement格式更加严谨,目前网易RDS中全部MySQL binlog都是基于row格式的,至于每张表必须含有主键或惟一性索引的要求也正是DBA对产品开发者耳提面命的要求,所以这两个限制能够忽略不计。
表的数据重分布
表的数据重分布与扩容缩容本质上是同样的,在应用场景上体现为表更换分区字段,或更换均衡策略。以更换分区字段为例,更换分区字段后,全部数据都要从新通过新的数据分布表来进行分布。表的数据重分布在需求上其实更为常见,好比一个项目初期选择分区字段上没考虑清楚,致使数据增加后数据分布不均匀,就要考虑更换一个更加均衡的分区字段,又如业务想对个别表作存储隔离,就须要把这个表从当前共享均衡策略中迁移到一个隔离的均衡策略里。
在DDB老的数据迁移方案中,并无针对表的迁移方案,在实际操做过程当中,通常DBA会根据场景的不一样本身设计方案,多多少少都会要求产品方容忍必定的停服时间,而在引入Hamal的解决方案后,表的数据重分布也迎刃而解。
由于Hamal的全量复制和增量复制都是由第三方工具完成,能够高度定制化。在Hamal解决方案中,表的数据迁移与库的扩容缩容并无本质区别,只是在全量复制和增量复制时过滤数据的方式不一样。
数据同步
与DDB的数据迁移场景相比,数据库之间的数据同步需求更加常见,因为MySQL自带的replication原生复制方案,尤为最近几个版本的并行复制效果尚可,你们每每会倾向使用MySQL自带的数据同步功能。可是MySQL基于replication的数据同步方案有三个限制:
MySQL原生复制只能作到点到点,没法点到组
MySQL原生复制依赖MySQL底层,上层没法干预,同步方式没法定制
虽然新版本MySQL并行复制速度尚可,但在跨机房同步等场景中依然不够可观
在使用DDB的业务场景中,经常须要用到点到组的数据同步,最经典的莫过于“好友关系问题”和“买家卖家问题”。以易信的好友关系为例,好友表有两个字段,分别为userId和friendId,业务的查询需求能够分为如下两类:
查询我有哪些好友
查询我是哪些人的好友
“查询个人好友”场景比较多,会占主要部分,“查询我是哪些人的好友”用于判断评论可见性和好友推荐等场景,请求量也不小,在这种状况下,选择userId仍是friendId做为分区字段成为一个难题,不管选择哪一个字段都会顾此失彼。在这种情况下,咱们的推荐作法是使用冗余表,假设主表为friendship,主键和分区字段为userId,为friendship建冗余表friendship_reverse,主键和分区字段为friendId,这样“查询个人好友”时走表friendship,“查询我是哪些人的好友”时走表friendship_reverse。以下图:
为了保证业务的正确性,须要确保friendship表和friendship_reverse表数据严格一致,若是在业务层作事务性双写,因为两表分区字段不一样,双写会成为一个分布式事务,目前任何基于两阶段协议实现的分布式事务都没法作到单机事务保证严格的ACID,所以并不建议在采用双写的方式解决冗余问题。
推荐的作法是经过第三方的数据同步中间件来保证冗余表friendship_reverse的数据一致性,对于MySQL原生复制,因为分区字段不一样,由一个源数据库产生的数据可能要同步到不一样的终点数据库中,即所谓的“点到组”,另外,同步的两个表名自己也并不相同,在这里MySQL的原生复制功能没法定制的功能体现无疑。
Hamal engine能够很好地解决这类数据同步问题,咱们须要作的是部署与源数据库实例数量相同的Hamal engine进程,根据同步条件稍做配置。每一个hamal engine会以一个slave的身份向一个源数据库拉取binlog,根据同步条件过滤binlog事件,再根据apply配置生成相应SQL应用到目标数据库中,因为friendship_reverse在DDB中有已经定义好的数据分布表,hamal在apply过程当中并不须要知道目的节点信息,只要经过DDB中间件去apply便可完成透明的数据路由,这也是Hamal和DDB配合使用的妙处之一。
除了业务上的冗余表外,跨机房的数据同步也是Hamal能够大展拳脚的应用场景之一,在2015年的中国数据库技术大会上,阿里巴巴集团第一次向外界分享了基于DRC的异步多活的多机房数据同步解决方案,而DRC的核心就是一个个旁路的拉取binlog并进行定制化跨机房传输并apply binlog的进程。在这方面hamal与DRC的设计殊途同归,将来能够基于hamal来定制咱们本身的跨机房数据同步方案。
数据分发
数据分发是一个较为偏向业务的应用场景,例如经过数据分发更新缓存,建立更新索引等。由Hamal engine拉取源数据库的binlog,根据配置生成相应的下游任务,这个下游任务能够是向消息队列里插入一条数据,也能够直接更新缓存,调用RPC等。阿里相似的“精卫”系统已在线上为淘宝各类业务线提供了几年的稳定服务。
Hamal特性与原理简析
在介绍DDB数据迁移场景中,对Hamal工做原理已作过简要介绍,Hamal分为Hamal engine和Hamal admin两大部件,Hamal engine是真正负责数据迁移,数据分发和数据同步的进程,而hamal admin是负责管理hamal engine,调度任务的外部组件集合。目前Hamal engine已经开发完成,而且开始在实际的数据迁移场景中发挥做用。Hamal engine具备的特性以下:
支持行级和事务级并行复制,追赶binlog速度远快于MySQL原生复制
支持断点续传,在源端或目的端出现问题后,能够暂停任务,修复问题后恢复任务
保证数据一致性,其中行级事务并行复制适用于迁移场景,只保证最终一致性,在牺牲彻底实时一致性的同时知足了高速的需求
Hamal engine的特性在于快和幂等,快保证了hamal在数据迁移场景下很好地缩短任务周期,也有益于数据同步场景下的同步实时性,幂等则保证了hamal engine只须要定时记录一个事务记录点,便可在不破坏数据一致性的前提下重启任务。
hamal实现幂等的核心思想是使用replace代替insert和update,保证数据最终的值必定会生效在目标数据库里。使用replace的前提是要保证同一份数据的操做必须排队执行。
同时,为了保证数据的正确性,Hamal engine也必须保证即使是在并行复制下,同一份数据的apply也必须按照在binlog位置中的顺序来执行,这个过程相似于MySQL中的“行锁”,只不过Hamal engine中没有保存真实的数据结构,也难以实现一套相似于MySQL的完整锁体系,Hamal engine内部的“行锁”机制是经过一个有向无环图算法,为一份数据在binlog不一样位置上的修改建议依赖关系,能够理解为同一行上的修改经过咱们的有向无环图算法造成了一个执行队列,这个队列中只有上一个节点执行完成才可能触发下一个节点的执行。
Hamal engine的核心组件以下所示:
其中,Extractor模块经过MySQL协议向源端MySQL拉取binlog,并存储到本地盘或云硬盘的relaylog中,这方面与MySQL原生复制中的IO thread彻底一致。Poller是一个独立线程,会随时读取relaylog中最新的二进制数据,并转换为Hamal能够处理的Event,根据配置的不一样,一个event能够表明一行数据的更改,也能够表明一个事务。一个event在被回放以前首先会进入一个叫作EventGraph的数据结构中计算与当前系统中全部event的依赖关系,对同一份数据,若是发现有前后更改的冲突,EventGraph会按照依赖关系将后面的更改先存储起来,当以前的更改回放完成后,再触发以后的更改。若是一个event再也不依赖系统中任意其余event,这个event将会被丢入SlaveQueue,并最终被某个applier线程回放掉。
对于一张表,表上全部惟一性索引,包括主键都会对应一个EventGraph,由于只有经过惟一性冲突才能判断数据之间是否存在依赖。对于惟一健值更改的状况,还须要将更改分裂为delete和replace,而且分别将delete和replace的键值拿出来计算依赖。
总结
大数据时代,数据每每表明了一个互联网企业的核心价值,怎样去玩数据也是成为各个企业重点关注的问题。在这个背景下,相似于Hamal这种基于MySQL的数据迁移,数据同步和数据分发解决方案能够发挥不俗的价值,一样类型的系统,在阿里就有好几套。
咱们对Hamal的指望,是以hamal engine实现一个核心架构和一套简洁的接口,在为DDB数据迁移,MySQL数据同步,分发等场景分别建立不一样的插件实现。最后经过Hamal admin实现各类解决方案的串联和自动化,由于Hamal是一个独立于MySQL的中间件系统,咱们能够在上面充分发挥"第三方"的优点,目前高速的并行复制和断点续传都是Hamal相比于其余数据复制方案的优点。将来,咱们也能够在Hamal上定制更多的高可用功能,从而将它升级为一个多机房的数据同步解决方案。
本文来自网易云社区,经做者马进受权发布
相关文章:
【推荐】 本身动手写ImpalaUDF
【推荐】 网易对象存储NOS图床神器