MySQL数据同步
MySQL数据同步
  在当今互联⽹⾏业,⼤多数⼈互联⽹从业者对"单元化"、"异地多活"这些词汇已经⽿熟能详。⽽数据同步是异地多活的基础,所有具备数据存储能⼒的组件如:数据库、缓存、MQ等,数据都可以进⾏同步,形成⼀个庞⼤⽽复杂的数据同步拓扑。
  本⽂将先从概念上介绍单元化、异地多活、就近访问等基本概念。之后,将以数据库为例,讲解在数据同步的情况下,如何解决数据回环、数据冲突、数据重复等典型问题。
1 什么是单元化
  如果仅仅从"单元化”这个词汇的⾓度来说,我们可以理解为将数据划分到多个单元进⾏存储。"单元"是⼀个抽象的概念,通常与数据中⼼(IDC)概念相关,⼀个单元可以包含多个IDC,也可以只包含⼀个IDC。本⽂假设⼀个单元只对应⼀个IDC。
  考虑⼀开始只有⼀个IDC的情况,所有⽤户的数据都会写⼊同⼀份底层存储中,如下图所⽰:
这种架构是⼤多数据中⼩型互联⽹公司采⽤的⽅案,存在以下⼏个问题:
  1、不同地区的⽤户体验不同。⼀个IDC必然只能部署在⼀个地区,例如部署在北京,那么北京的⽤户
访问将会得到快速响应;但是对于上海的⽤户,访问延迟⼀般就会⼤⼀点,上海到北京的⼀个RTT可能有20ms左右。
  2、容灾问题。这⾥容灾不是单台机器故障,⽽是指机房断电,⾃然灾害,或者光纤被挖断等重⼤灾害。⼀旦出现这种问题,将⽆法正常为⽤户提供访问,甚⾄出现数据丢失的情况。这并不是不可能,例如:2015年,⽀付宝杭州某数据中⼼的光缆就被挖断过;2018年9⽉,云栖⼤会上,蚂蚁⾦服当场把杭州两个数据中⼼的⽹线剪断。
  为了解决这些问题,我们可以将服务部署到多个不同的IDC中,不同IDC之间的数据互相进⾏同步。如下图:
  通过这种⽅式,我们可以解决单机房遇到的问题:
  1、⽤户体验。不同的⽤户可以选择离⾃⼰最近的机房进⾏访问
  2、容灾问题。当⼀个机房挂了之后,我们可以将这个机房⽤户的流量调度到另外⼀个正常的机房,由于不同机房之间的数据是实时同步的,⽤户流量调度过去后,也可以正常访问数据 (故障发⽣那⼀刻的少部分数据可能会丢失)。
需要注意的是,关于容灾,存在⼀个容灾级别的划分,例如:单机故障,机架(rack)故障,机房故障,
城市级故障等。我们这⾥只讨论机房故障和城市故障。
机房容灾 : 上⾯的案例中,我们使⽤了2个IDC,但是2个IDC并不能具备机房容灾能⼒。⾄少需要3个IDC,例如,⼀些基于多数派协议的⼀致性组件,如
寺庙
zookeeper,redis、etcd、consul等,需要得到⼤部分节点的同意。例如我们部署了3个节点,在只有2个机房的情况下,必然是⼀个机房部署2个节点,⼀个机房部署⼀个节点。当部署了2个节点的机房挂了之后,只剩下⼀个节点,⽆法形成多数派。在3机房的情况下,每个机房部署⼀个节点,任意⼀个机房挂了,还剩2个节点,还是可以形成多数派。这也就是我们常说的"两地三中⼼”。
城市级容灾:在发⽣重⼤⾃然灾害的情况下,可能整个城市的机房都⽆法访问。⼀些组件,例如蚂蚁的ocean base,为了达到城市级容灾的能⼒,使⽤的是"三地五中⼼"的⽅案。这种情况下,3个城市分别拥有2、2、1个机房。当整个城市发⽣灾难时,其他两个城市依然⾄少可以保证有3个机房依然是存活的,同样可以形成多数派。
  ⼩结:如果仅仅是考虑不同地区的⽤户数据就近写⼊距离最近的IDC,这是纯粹意义上的”单元化”。不同单元的之间数据实时进⾏同步,相互备份对⽅的数据,才能做到真正意义上"异地多活”。实现单元化,技术层⾯我们要解决的事情很多,例如:流量调度,即如何让⽤户就近访问附近的IDC;数据互通,如何实现不同机房之间数据的相互同步。流量调度不在本⽂的讨论范畴内,数据同步是本⽂讲
解的重点。
2 如何实现数据同步
  需要同步的组件有很多,例如数据库,缓存等,这⾥以多个Mysql集之间的数据同步为例进⾏讲解,实际上缓存的同步思路也是类似。
2.1 基础知识
  为了了解如何对不同mysql的数据相互进⾏同步,我们先了解⼀下mysql主从复制的基本架构,如下图所⽰:
  通常⼀个mysql集有⼀主多从构成。⽤户的数据都是写⼊主库Master,Master将数据写⼊到本地⼆进制⽇志binary log中。从库Slave启动⼀个IO线程(I/O Thread)从主从同步binlog,写⼊到本地的relay log中,同时slave还会启动⼀个SQL Thread,读取本地的relay log,写⼊到本地,从⽽实现数据同步。
基于这个背景知识,我们就可以考虑⾃⼰编写⼀个组件,其作⽤类似与mysql slave,也是去主库上拉取binlog,只不过binlog不是保存到本地,⽽是将binlog转换成sql插⼊到⽬标mysql集中,实现数据的同步。
这并⾮是⼀件不可能完成的事,MySQL官⽹上已经提供好所有你⾃⼰编写⼀个mysql slave 同步binlog所需的相关背景知识,访问这个链接:
sql/doc/internals/en/client-server-protocol.html ,你将可以看到mysql 客户端与服务端的通信协议。下图红⾊框中展⽰了Mysql主从复制的相关协议:当然,笔者的⽬的并不是希望读者真正的按照这⾥的介绍尝试编写⼀个mysql 的slave,只是想告诉读者,模拟mysql slave拉取binlog并⾮是⼀件很神奇的事,只要你的⽹络
基础知识够扎实,完全可以做到。然⽽,这是⼀个庞⼤⽽复杂的⼯作。以⼀⼈之⼒,要完成这个⼯作,需要占⽤你⼤量的时间。好在,现在已经有很多开源的组件,已经实现了按照这个协议可以模拟成⼀个mysql的slave,拉取binlog。例如:
阿⾥巴巴开源的canal
美团开源的puma
linkedin开源的databus        ...
你可以利⽤这些组件来完成数据同步,⽽不必重复造轮⼦。假设你采⽤了上⾯某个开源组件进⾏同步,需要明⽩的是这个组件都要完成最基本的2件事:从源库拉取binlog并进⾏解析,笔者把这部分功
能称之为binlog syncer ;将获取到的binlog转换成SQL插⼊⽬标库,这个功能称之为sql writer。
为什么划分成两块独⽴的功能?因为binlog订阅解析的实际应⽤场景并不仅仅是数据同步,如下图:
如图所⽰,我们可以通过binlog来做很多事,如:
实时更新搜索引擎,如es中的索引信息
实时更新redis中的缓存
发送到kafka供下游消费,由业务⽅⾃定义业务逻辑处理等
...
因此,通常我们把binlog syncer单独作为⼀个模块,其只负责解析从数据库中拉取并解析binlog,并在内存中缓存(或持久化存储)。另外,binlog syncer另外提⼀个sdk,业务⽅通过这个sdk从binlog syncer中获取解析后的binlog信息,然后完成⾃⼰的特定业务逻辑处理。
显然,在数据同步的场景下,我们可以基于这个sdk,编写⼀个组件专门⽤于将binlog转换为sql,插⼊⽬标库,实现数据同步,如下图所⽰:
北京⽤户的数据不断写⼊离⾃⼰最近的机房的DB,通过binlog syncer订阅这个库binlog,然后下游的binlog writer将binlog转换成SQL,插⼊到⽬标库。上海⽤户类似,只不过⽅向相反,不再赘述。通过这种⽅式,我们可以实时的将两个库的数据同步到对端。当然事情并⾮这么简单,我们有⼀些重要的事情需要考虑。
2.2 如何获取全量+增量数据?
通常,mysql不会保存所有的历史binlog。原因在于,对于⼀条记录,可能我们会更新多次,这依然是⼀条记录,但是针对每⼀次更新操作,都会产⽣⼀条binlog记录,这样就会存在⼤量的binlog,很快会将磁盘占满。因此DBA通常会通过⼀些配置项,来定时清理binlog,只保留最近⼀段时间内的binlog。
例如,官⽅版的mysql提供了expire_logs_days配置项,可以设置保存binlog的天数,笔者这⾥设置为0,表⽰默认不清空,如果将这个值设置⼤于0,则只会保存指定的天数。
  另外⼀些mysql 的分⽀,如percona server,还可以指定保留binlog⽂件的个数。我们可以通过show binary logs来查看当前mysql存在多少个binlog⽂件,如下图:通常,如果binlog如果从来没被清理过,那么binlog⽂件名字后缀通常是000001,如果不是这个值,则说明可能已经被清理过。当然,这也不是绝对,例如执⾏"reset master”命令,可以将所有的binlog清空,然后从000001重新开始计数。
  Whatever! 我们知道了,binlog可能不会⼀直保留,所以直接同步binlog,可能只能获取到部分数据。因此,通常的策略是,由DBA先dump⼀份源库的完整数据快照,增量部分,再通过binlog订阅解析进⾏同步。
2.2 如何解决重复插⼊
  考虑以下情况下,源库中的⼀条记录没有唯⼀索引。对于这个记录的binlog,通过sql writer将binlog转换成sql插⼊⽬标库时,抛出了异常,此时我们并不知道知道是否插⼊成功了,则需要进⾏重试。如果之前已经是插⼊⽬标库成功,只是⽬标库响应时⽹络超时(socket timeout)了,导致的异常,这个时候重试插⼊,就会存在多条记录,造成数据不⼀致。
  因此,通常,在数据同步时,通常会限制记录必须有要有主键或者唯⼀索引。
2.3 如何解决唯⼀索引冲突
  由于两边的库都存在数据插⼊,如果都使⽤了同⼀个唯⼀索引,那么在同步到对端时,将会产⽣唯⼀索引冲突。对于这种情况,通常建议是使⽤⼀个全局唯⼀的分布式ID⽣成器来⽣成唯⼀索引,保证不会产⽣冲突。
  另外,如果真的产⽣冲突了,同步组件应该将冲突的记录保存下来,以便之后的问题排查。
2.4 对于DDL语句如何处理
  如果数据库表中已经有⼤量数据,例如千万级别、或者上亿,这个时候对于这个表的DDL变更,将会变得⾮常慢,可能会需要⼏分钟甚⾄更长时间,⽽DDL操作是会锁表的,这必然会对业务造成极⼤的影响。
  因此,同步组件通常会对DDL语句进⾏过滤,不进⾏同步。DBA在不同的数据库集上,通过⼀些在线DDL⼯具(如gh-ost),进⾏表结构变更。
2.5 如何解决数据回环问题
  数据回环问题,是数据同步过程中,最重要的问题。我们针对INSERT、UPDATE、DELETE三个操作来分别进⾏说明:
INSERT操作
  假设在A库插⼊数据,A库产⽣binlog,之后同步到B库,B库同样也会产⽣binlog。由于是双向同步,这条记录,⼜会被重新同步回A库。由于A库应存在这条记录了,产⽣冲突。
UPDATE操作
  先考虑针对A库某条记录R只有⼀次更新的情况,将R更新成R1,之后R1这个binlog会被同步到B库,B库⼜将R1同步会A库。对于这种情况下,A库将不会产⽣binlog。因为A库记录当前是R1,B库同步回来的还是R1,意味着值没有变。
  在⼀个更新操作并没有改变某条记录值的情况下,mysql是不会产⽣binlog,相当于同步终⽌。下图演⽰了当更新的值没有变时,mysql实际上不会做任何操作:上图演⽰了,数据中原本有⼀条记录(1,"tianshouzhi”),之后执⾏⼀个update语句,将id=1的记录的name值再次更新为”tianshouzhi”,意味着值并没有变更。这个时候,我们看到mysql 返回的影响的记录函数为0,也就是说,并不会产⽣真是的更新操作。
然⽽,这并不意味UPDATE 操作没有问题,事实上,其⽐INSERT更加危险。考虑A库的记录R被连续更新了2次,第⼀次更新成R1,第⼆次被更新成R2;这两条记录变更信息都被同步到B库,B也产⽣了R1和R2。由于B的数据也在往A同步,B的R1会被先同步到A,⽽A现在的值是R2,由于值不⼀样,将会被更新成R1,并产⽣新的binlog;此时B的R2再同步会A,发现A的值是R1,⼜更新成R2,也产⽣binlog。由于B同步回A的操作,让A⼜产⽣了新的binlog,A⼜要同步到B,如此反复,陷⼊⽆限循环中。
DELETE操作
同样存在先后顺序问题。例如先插⼊⼀条记录,再删除。B在A删除后,⼜将插⼊的数据同步回A,接着再将A的删除操作也同步回A,每次都会产⽣binlog,陷⼊⽆限回环。
对老师的赞美关于数据回环问题,笔者有着⾎的教训,曾经因为笔者的误操作,将⼀个库的数据同步到了⾃⾝,最终也导致⽆限循环,原因分析与上述提到的UPDATE、DELETE操作类似,读者可⾃⾏思考。
针对上述数据同步到过程中可能会存在的数据回环问题,最终会导致数据⽆限循环,因此我们必须要解决这个问题。由于存在多种解决⽅案,我们将在稍后统⼀进⾏讲解。
2.6 数据同步架构设计
现在,让我们先把思路先从解决数据同步的具体细节问题转回来,从更⾼的层⾯讲解数据同步的架构应该如何设计。稍后的内容中,我们将讲解各种避免数据回环的各种解决⽅案。
前⾯的架构中,只涉及到2个DB的数据同步,如果有多个DB数据需要相互同步的情况下,架构将会变得⾮常复杂。例如:
  这个图演⽰的是四个DB之间数据需要相互同步,这种拓扑结构⾮常复杂。为了解决这种问题,我们可以将数据写⼊到⼀个数据中转站,例如MQ中进⾏保存,如下:
  我们在不同的机房各部署⼀套MQ集,这个机房的binlog syncer将需要同步的DB binlog数据写⼊MQ对应的Topic中。对端机房如果需要同步这个数据,只需要通过binlog writer订阅这个topic,消费topic中的binlog数据,插⼊到⽬标库中即可。⼀些MQ⽀持consumer group的概念,不同的consumer group的消费位置offset相互隔离,从⽽达到⼀份数据,同时供多个消费者进⾏订阅的能⼒。
  当然,⼀些binlog订阅解析组件,可能实现了类似于MQ的功能,此时,则不需要独⽴部署MQ。
3 数据据回环问题解决⽅案
数据回环问题有多种解决⽅案,通过排除法,⼀⼀进⾏讲解。如果 金泰妍
3.1 同步操作不⽣成binlog
在mysql中,我们可以设置session变量,来控制当前会话上的更新操作,不产⽣binlog。这样当往⽬标库插⼊数据时,由于不产⽣binlog,也就不会被同步会源库了。为了
演⽰这个效果,笔者清空了本机上的所有binlog(执⾏reset master),现在如下图所⽰:
  忽略这两个binlog event,binlog⽂件格式最开始就是这两个event。
  接着,笔者执⾏set sql_log_bin=0,然后插⼊⼀条语句,最后可以看到的确没有产⽣新的binlog事件:
通过这种⽅式,貌似可以解决数据回环问题。⽬标库不产⽣binlog,就不会被同步会源库。但是,答案是否定的。我们是往⽬标库的master插⼊数据,如果不产⽣binlog,⽬
标库的slave也⽆法同步数据,主从数据不⼀致。所以,需要排除这种⽅案。
提⽰:如果恢复set sql_log_bin=1,插⼊语句是会产⽣binlog,读者可以⾃⾏模拟。
3.2 控制binlog同步⽅向
既然不产⽣binlog不能解决问题。那么换⼀种思路,可以产⽣binlog。当把⼀个binlog转换成sql时,插⼊某个库之前,我们先判断这条记录是不是原本就是这个库产⽣的,
如果是,那么就抛弃,也可以避免回环问题。
现在问题就变为,如何给binlog加个标记,表⽰其实那个mysql集产⽣的。这也有⼏种⽅案,下⾯⼀⼀讲述。
3.2.1 ROW模式下的SQL
mysql主从同步,binlog复制⼀般有3种模式。STATEMENT,ROW,MIXED。默认情况下,STATEMENT模式只记录SQL语句,ROW模式只记录字段变更前后的
值,MIXED模式是⼆者混合。 binlog同步⼀般使⽤的都是ROW模式,⾼版本Mysql主从同步默认也是ROW模式。
  我们想采取的⽅案是,在执⾏的SQL之前加上⼀段特殊标记,表⽰这个SQL的来源。例如
/*IDC1:DB1*/insert into users(name) values("tianbowen")
其中/*IDC1:DB1*/是⼀个注释,表⽰这个SQL原始在是IDC1的DB1中产⽣的。之后,在同步的时候,解析出SQL中的IDC信息,就能判断出是不是⾃⼰产⽣的数据。
然⽽,ROW模式下,默认只记录变更前后的值,不记录SQL。所以,我们要通过⼀个开关,让Mysql在ROW模式下也记录INSERT、UPDATE、DELETE的SQL语句。具体
做法是,在mysql的配置⽂件中,添加以下配置:
binlog_rows_query_log_events =1
这个配置可以让mysql在binlog中产⽣ROWS_QUERY_LOG_EVENT类型的binlog事件,其记录的就是执⾏的SQL。
通过这种⽅式,我们就记录下的⼀个binlog最初是由哪⼀个集产⽣的,之后在同步的时候,sql writer判断⽬标机房和当前binlog中包含的机房相同,则抛弃这条数据,从⽽
避免回环。电水壶什么牌子的好
这种思路,功能上没问题,但是在实践中,确⾮常⿇烦。⾸先,让业务对执⾏的每条sql都加上⼀个这样的标识,⼏乎不可能。另外,如果忘记加了,就不知道数据的来源
曾晖了。如果采⽤这种⽅案,可以考虑在数据库访问层中间件层⾯添加⽀持在sql之前增加/*..*/的功能,统⼀对业务屏蔽。即使这样,也不完美,不能保证所有的sql都通过中间件来来
写⼊,例如DBA的⼀些⽇常运维操作,或者⼿⼯通过mysql命令⾏来操作数据库时,肯定会存在没有添加机房信息的情况。
总的来说,这个⽅案不是那么完美。
3.2.2 通过附加表
这种⽅案⽬前很多知名互联⽹公司在使⽤。⼤致思路是,在db中都加⼀张额外的表,例如叫direction,记录⼀个binlog产⽣的源集的信息。例如
CREATE TABLE `direction` (  `idc` varchar(255) not null,  `db_cluster` varchar(255) not null,) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
  idc字段⽤于记录某条记录原始产⽣的IDC,db_cluster⽤于记录原始产⽣的数据库集(注意这⾥要使⽤集的名称,不能是server_id,因为可能会发⽣主从切换)。
  假设⽤户在IDC1的库A插⼊的⼀条记录(也可以在事务中插⼊多条记录,单条记录,即使不开启事务,mysql默认也会开启事务):
BEGIN;insert into users(name) values("tianshouzhi”);COMMIT;
  那么A库数据binlog通过sql writer同步到⽬标库B时,sql writer可以提前对事务中的信息可以进⾏⼀些修改,,如下所⽰:
BEGIN;#往⽬标库同步时,⾸先额外插⼊⼀条记录,表⽰这个事务中的数据都是A产⽣的。insert into direction(idc,db_cluster) values("IDC1”,"DB_A”)#插⼊原来的记录信息insert into users(name) values("tianshouzhi”);COMMIT;
  之后B库的数据往A同步时,就可以根据binlog中的第⼀条记录的信息,判断这个记录原本就是A产⽣的,进⾏抛弃,通过这种⽅式来避免回环。这种⽅案已经已经过很多的
公司的实际验证。
3.2.3 通过GTID
  Mysql 5.6引⼊了GTID(全局事务id)的概念,极⼤的简化的DBA的运维。在数据同步的场景下,GTID依然也可以发挥极⼤的威⼒。
  GTID 由2个部分组成:
  server_uuid:transaction_id
  其中server_uuid是mysql随机⽣成的,全局唯⼀。transaction_id事务id,默认情况下每次插⼊⼀个事务,transaction_id⾃增1。注意,这⾥并不会对GTID进⾏全⾯的介绍,关晓彤可以射的照片
仅说明其在数据同步的场景下,如何避免回环、数据重复插⼊的问题。
  GTID提供了⼀个会话级变量gtid_next,指⽰如何产⽣下⼀个GTID。可能的取值如下:
AUTOMATIC: ⾃动⽣成下⼀个GTID,实现上是分配⼀个当前实例上尚未执⾏过的序号最⼩的GTID。
ANONYMOUS: 设置后执⾏事务不会产⽣GTID,显式指定的GTID。
  默认情况下,是AUTOMATIC,也就是⾃动⽣成的,例如我们执⾏sql:
insert into users(name) values("tianbowen”);
  产⽣的binlog信息如下:
  可以看到,GTID会在每个事务(Query->...->Xid)之前,设置这个事务下⼀次要使⽤到的GTID。
  从源库订阅binlog的时候,由于这个GTID也可以被解析到,之后在往⽬标库同步数据的时候,我们可以显⽰的的指定这个GTID,不让⽬标⾃动⽣成。也就是说,往⽬标库,
同步数据时,变成了2条SQL:
SET GTID_NEXT= '09530823-4f7d-11e9-b569-00163e121964:1’insert into users(name) values("tianbowen")
  由于我们显⽰指定了GTID,⽬标库就会使⽤这个GTID当做当前事务ID,不会⾃动⽣成。同样,这个
操作也会在⽬标库产⽣binlog信息,需要同步回源库。再往源库同步时,
我们按照相同的⽅式,先设置GTID,在执⾏解析binlog后得到的SQL,还是上⾯的内容
SET GTID_NEXT= '09530823-4f7d-11e9-b569-00163e121964:1'insert into users(name) values("tianbowen")
由于这个GTID在源库中已经存在了,插⼊记录将会被忽略,演⽰如下:
mysql> SET GTID_NEXT= '09530823-4f7d-11e9-b569-00163e121964:1';Query OK, 0 rows affected (0.00 sec)mysql> insert into users(name) values("tianbowen");Query OK, 0 rows affected (0.01 sec) #注意这⾥,影响的记录⾏数为0  注意这⾥,对于⼀条insert语句,其影响的记录函数居然为0,也就会插⼊并没有产⽣记录,也就不会产⽣binlog,避免了循环问题。
  如何做到的呢?mysql会记录⾃⼰执⾏过的所有GTID,当判断⼀个GTID已经执⾏过,就会忽略。通过如下sql查看:
mysql> show global variables like "gtid_executed";
+---------------+------------------------------------------+
| Variable_name | Value                                    |
+---------------+------------------------------------------+
| gtid_executed | 09530823-4f7d-11e9-b569-00163e121964:1-5 |
+---------------+------------------------------------------+
上述value部分,冒号":"前⾯的是server_uuid,冒号后⾯的1-5,是⼀个范围,表⽰已经执⾏过1,2,3,4,5这个⼏个transaction_id。这⾥就能解释了,在GTID模式的情
况下,为什么前⾯的插⼊语句影响的记录函数为0了。
显然,GTID除了可以帮助我们避免数据回环问题,还可以帮助我们解决数据重复插⼊的问题,对于⼀条没有主键或者唯⼀索引的记录,即使重复插⼊也没有,只要GTID已
经执⾏过,之后的重复插⼊都会忽略。
当然,我们还可以做得更加细致,不需要每次都往⽬标库设置GTID_NEXT,这毕竟是⼀次⽹络通信。sql writer在往⽬标库插⼊数据之前,先判断⽬标库的server_uuid是不
是和当前binlog事务信息携带的server_uuid相同,如果相同,则可以直接丢弃。查看⽬标库的gtid,可以通过以下sql执⾏:
mysql> show variables like "server_uuid";
+---------------+--------------------------------------+
| Variable_name | Value                                |
+---------------+--------------------------------------+
| server_uuid  | 09530823-4f7d-11e9-b569-00163e121964 |
+---------------+--------------------------------------+
GTID应该算是⼀个终极的数据回环解决⽅案,mysql原⽣⾃带,⽐添加⼀个辅助表的⽅式更轻量,开销也更低。需要注意的是,这倒并不是⼀定说GTID的⽅案就⽐辅助表
好,因为辅助表可以添加机房等额外信息。在⼀些场景下,如果下游需要知道这条记录原始产⽣的机房,还是需要使⽤辅助表。
4 开源组件介绍canal/otter
  前⾯深⼊讲解了单元化场景下数据同步的基础知识。读者可能⽐较感兴趣的是,哪些开源组件在这些⽅⾯做的⽐较好。笔者建议的⾸选,是canal/otter组合。
  canal的作⽤就是类似于前⾯所述的binlog syncer,拉取解析binlog。otter是canal的客户端,专门⽤于进⾏数据同步,类似于前⽂所讲解的sql writer。并且,canal的最新版
本已经实现了GTID。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。