在Binlog中可以这样使用,转载请联系CafeLatte公众号。不知道你是否还在为以下问题所困扰:当你使用redis或者其他中间件进行缓存时,经常会发现缓存中的数据和数据库中的数据不一致,只能通过定时任务做一些限制或缓存过期。当你用ES作为搜索工具,使用双写方式的时候,你还在担心ES和数据库不是一个事务。当你需要迁移数据时,你仍然在使用双写的方式。如果是同一个数据库还好,但是如果是不同的数据库,transaction就无法保证,那么数据的一致性也是个问题,会写很多repairjob,检查Job。相信很多同学在业务中应该都遇到过这些问题,也可能是因为这些问题往往会增加很多工作量或者导致一些数据不一致的故障。那么如何才能比较简单的解决这些问题呢?我们想一想这个问题的本质是什么?需要保证我们的数据无论是在redis中还是在es中,都和我们的mysql是一致的,本质上就是数据复制。一想到数据复制,熟悉Mysql的朋友就会说:Mysql的主备不也是数据复制吗?如果我们模仿Mysql的主从复制,那么我们的数据同步就非常容易了。Mysqlmaster-slave既然我们可以模仿Mysql的主从复制来完成我们的需求,那我们就需要先了解一下mysql主从的原理,如下图所示:Stpe1:Mysql作为master需要更新数据在每个事务完成之前,操作记录被串行写入binlog文件,存储在本地磁盘中。Step2:在我们的salveserver中开启一个I/OThread,如果读取到,它会不断的从binlog中读取。如果进度跟上了master,就进入休眠状态,等待master产生新的事件。所有读取的数据都会写入中继日志(relaylog)。Step3:SQLThread会读取relaylog,依次执行日志中的SQLevents,从而与主库中的数据保持一致。在主从复制的过程中,最重要的就是binlog,从库会根据binlog的信息复制一份主库的数据。如果我们能够在业务代码中获取到binlog,将binlog数据复制到redis或者es中,那么我们就完全不用担心数据的一致性问题了。binlogbinlog(BinaryLog)顾名思义,就是Mysql中的二进制日志,记录了Mysql对数据库所做的所有更改操作。Binlog也是server层产生的日志,跟我们的存储引擎没有关系。无论您使用哪种存储引擎,都可以使用我们的binlog。binlog格式在binlog中有三种格式,分别是:Statement、Row、Mixed。可以通过showvariableslike'binlog_format'查看当前数据库的binlog格式。如下图,是一个Row格式的binlog:StatementStatement是Statement类型,它会把每条修改数据的Sql记录到binlog中。?优点:占空间比最小,没有被修改的字段不会被记录。与其他模式相比,它减少了很多日志灯并提高了I/O性能。?缺点:异构系统使用不方便。比如redis缓存拷贝时,很难模拟mysql的slave操作,需要重新校验一次数据。而且slave也会有问题,比如使用一些UUID函数,slave在replay的时候,不能保证双方一致。我们可以查看Statement的日志内容是什么?我们可以在这里输入命令:showmasterstatus;查看我们当前master使用的binlog,如下图:然后使用命令showbinlogeventsin'mysql-bin.000003',查看这个日志的内容:可以发现我们所有的操作都会被携带在一个完整的交易中。如果事务没有提交,它就不会出现在我们的binlog中。你可以下来试验一下。我们在数据库中更新的原始sql会被完整的记录下来。RowRow模式不同于Statement,它会记录每一行所有修改的数据:优点:异构系统也可以很方便的同步数据,不会有UUID函数的问题,无论什么情况都可以复制。缺点:数据量较大,比如update语句,也会记录更新前的每个字段和更新后的每个字段。日志量比较大,对I/O有一定的影响。同样,我们也查看一下内容:在showbinlogeventsin'mysql-bin.000004'命令中,我们发现无法查看到我们在事务中的具体数据。这时候就需要我们的工具来帮助mysqlbinlog了,他也在mysql的bin目录下,我们可以直接调用,输入命令/usr/local/mysql/bin/mysqlbinlog--base64-output=decode-rows-vmysql-bin.000004,我们可以看到:这里是一条update语句,他不仅展示了原来的值,还展示了修改后的值。这里需要注意的是binlog_row_image是用来判断该行是否会记录原始值的。默认是FULL,表示会记录,就是上面的情况。还有一个参数minimal,表示只记录更新的值。Mixed在混合模式下,MySQL默认还是使用statement格式记录,但是一旦判断可能存在数据不一致(UUID功能),就会使用row格式记录。我们目前默认使用Row模式。在Row模式下,数据可以异构方便。其实Row模式对I/O的影响在业务上并不是特别明显。Canal当我们知道了binlog是什么之后,我们就需要如何使用这个binlog。常见的binlog同步工具有:databus、canal、maxwell、阿里云dts等,这里不比较它们各自的优缺点,重点介绍canal。canal(github地址:https://github.com/alibaba/canal),翻译为waterway/pipe/ditch,主要目的是基于MySQL数据库增量日志分析,提供增量数据订阅和消费早期阿里巴巴因为杭州而有了美国双机房的部署,有跨机房同步的业务需求,实现方式主要是根据业务触发获取增量变更。从2010年开始,业务逐渐尝试解析数据库日志获取增量变化进行同步,并由此衍生出大量的增量数据库订阅消费业务。后来逐渐演变成阿里云的DTS项目。canal的大致原理就是模仿mysql的slave,不断的从master上拉取binlog,然后把binlog放到不同的地方,比如我们常见的消息队列:kafka,rocketmq等。当然,阿里云付费的dts也可以直接同步到redis、es或者其他一些存储介质上。canal的简单使用可以查看quickStart:https://github.com/alibaba/canal/wiki/QuickStart,这里不做过多介绍。接下来会详细介绍canal的整体架构,以及实现原理等等。Canal的整体架构CanalServer:一个Jvm可以理解为一个CanalServer,如果是一个集群的Canal,那么就会有多个CanalServer。CanalInstance:可以理解为一个job就是一个Instance。比如有一个把A库的binlog同步到A的消息队列,B库的binlog同步到B的消息队列,那么这就是两个不同的Instance。至于哪个Instance在哪个CanalServer上运行,需要看谁先抢占了ZK中的临时节点。如果分布足够均匀,在集群模式下可以减轻很多压力。CanalParser:用于拉取mysql-binlog并解析。EventSink:处理解析后的数据(过滤、合并等)。CanalEventStore:这个有点类似于slave中的relaylog,用于日志的中继存储,但是目前在canal中只支持存储在内存中,目前不支持磁盘存储。CanalParser、EventSink和CanalEventStore是Canal中非常重要的组件。它们之间的关系是这样的:CanalParser产生数据供EventSink处理,处理后的数据会存储在CanalEventStore中,然后MQ会继续从CanalEventStore中拉取最新的数据并post到MQ中。CanalParser下面说说Canal是如何伪装成slave在CanalParser中拉取数据的。AbstractEventParser.java类有以下步骤:Step1:建立数据库链接,生成slaveId,用于标识自己的slave。Step2:获取数据库的元信息,如binlogFormat、binRowImage等。Step3:使用showvariableslike'server_id'命令获取我们需要监控binlog服务的serverId。Step4:获取本次需要消耗的位置。如果是上一次存储的,就从上一次获取。如果不是,则需要通过showmasterstatus命令获取最新的Position来消费。Step5:执行dump操作,模拟slave发送注册slave请求,dumpbinlog请求,然后使用无限循环不断从binlog中拉取数据:Step6:将得到的二进制数据按照mysqlbinlog协议转换成logEntry,即方便后续处理。EventSinkEventSink会对上面获取到的logEntry进行处理:Filter:过滤空事务过滤heartbeat自定义过滤记录,这里使用Prometheus进行数据的统计上报。合并,现在有很多分库分表的业务需求。它们的数据源都来自于不同的Parser,但最终都需要聚合到同一个EventStore中。这个场景需要注意的是,我们会做时间合并控制,即尽量让各个分库的数据聚合起来,递增提交,避免出现数据不一致的情况。某个子数据库领先于其他或落后很多。EventStore我们看一下EventStore中提供的接口:可以看到EventStore其实就是一个简单的存储。canal中提供了MemoryEventStoreWithBuffer,用于传输内存中的数据。原理是通过RingBuffer(无锁,高性能队列)关于RingBuffer的资料可以参考我之前的文章Disruptor,你应该知道。RingBuffer在3.1中有详细解释。然后CanalMq通过EventStore不断获取数据发送数据。总结Canal里面其实还有一些其他的优化,比如修改表结构后的优化,gtid的一些优化等等,有兴趣的可以自己看,这里就不展开了。总结一下,本文主要是给大家讲一些binlog的知识和Canal的一些科普。希望大家以后在做异构系统的数据同步的时候可以多用binlog,用更简单的技术做更可靠的事情。.
