Canal binlog 日志 Dump 流程分析

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,高可用系列 2核4GB
简介: Canal binlog 日志 Dump 流程分析

Canal 的 dump 支持串行和并行模式两种模式,本篇重点梳理 dump 的核心流程,以便对 dump 过程有一个充分的了解,更好的理解 Canal 的实现原理与细节,下一篇中将重点关注Canal是如何引入并行模式来提高dump的性能,即并行编程相关的技巧。


从前面的文章我们得知 Canal binlog 日志解析的基本流程如下图所示:

e3d5123f04bbafed33d874e9bfae69eb.png


解析来重点梳理一下 dump 命令的发送逻辑,特别是日志的处理流程,一些基本的日志格式。


1、 dump 流程分析


在 Canal 中 dump 方法声明如下:


891f91b608189c8db519ca0b65542ddc.png

带有参数 MultiStageCoprocessor 为并行处理模式,底层使用了disruptor 高性能并发框架,下文将重点关注学习。我们今天重点来看一下串行dump的实现,其方法声明如下:

cdca713df60e1928d96e7e85413e6c2b.png

其方法参数说明如下:


  • String binlogfilename
     binlog 文件名称,例如  mysql-bin.000038。
  • Long binlogPosition
    在文件中的偏移量。
  • SinkFunction func
    每解析出一条binlog日志的处理函数。


接下来我们直奔主题,一起来看一下 MysqlConnection 关于 dump 的实现流程。

595a39d15e8d0ca62d008deda3dd0037.png

Step1:在发送dump之前先设置相关的参数。


  • set wait_timeout=9999999
    连接空闲超时时间,默认为8消息,用于 Canal Slave 的等待超时时间远大于默认值。
  • set net_write_timeout=1800
    网络写请求超时时间,针对正在进行数据读写的连接,该值默认为 60s。
  • set net_read_timeout=1800
    网络读请求超时时间,针对正在进行数据读写的连接,该值默认为 30s。
  • set names 'binary'
    设置服务端返回结果时不做编码转化,直接按照数据库的二进制编码进行发送,由客户端自己根据需求进行编码转化。
  • set @master_binlog_checksum= @@global.binlog_checksum
    设置master_binlog_checksum,因为在mysql5.6之后为binlog引入了checksum机制,例如crc32,canal作为mysql slave,需要与服务端相关参数保持一致。
  • set @slave_uuid=uuid()
    canal相对与mysql数据库服务而言就是一个从服务器,这个指令用于设置server_id,使用uuid,避免server_id重复。
  • SET @master_heartbeat_period=15
    设置客户端与服务端心跳发送间隔,默认为15s。

b52d1634bd5be6efdbc8bb687f927ce5.png

Step2:从主库查询binlog checksum,具体向主库发送 select @@global.binlog_checksum 语句。

92ef87ac282b0705e57cdbc30b1918ad.png

Step3:向MySQL Master 注册从节点,告知客户端的host、port、用户名与密码、serverId,具体实现是发送命令CODE为 0x15。

3087ccd752893371f6b50d858ec4fcad.png

Step4:向 MySQL Master 发送 dump 请求,MySQL是基于请求与应答模式,发送请求命令后,就会向网络通道中写入响应请求。(在这里大家不妨先大概思考一下如何读取 dump 命令的返回值,这部分虽然涉及到网络相关的知识,我在这边会稍微简单提一下)。

852a6b87df2388c6dcb5a9f7cb128254.png

Step5:构建 DirectLogFetcher对象,实现基于 socket 的日志拉取服务,并构建 LogDecoder 对象,用于解析 binlog 日志。

53c27cd52ec91cb08eb976501f977129.png

  • Step6:使用 while 循环反复拉取消息,通过通过 LogDecoder 对二进制流进行解析,提取一条完整的binlog事件,交给 SinkFunction 去处理,并且如果开启了半同步机制,则需要向master发送ACK。既然是while循环,该方法的退出条件还是值得我们关注的:
  • fetch.fetch()方法返回 false
  • SinkFunction 的 sink 方法 false,SinkFunction的详细处理流程将在下文介绍,这里先告知返回false的情况是 binlog 日志解析线程已停止运行。


上面粗略的介绍了 dump 命令的几个核心关键步骤,要想详细掌握其实现细节,我们必须继续深入探讨如下几个问题:


  • DirectLogFetcher 内部工作机制
  • LogDecoder binlog 日志解析
  • 发送Dump底层网实现思路


2、DirectLogFetcher 内部工作机制


2.1 DirectLogFetcher 类图


e1090d11ccc756c0d72a63b175ff0dbb.png

DirectLogFetcher的类继承体系如上图所示,我们来看一下其关键点:


  • LogBuffer
    日志buffer,主要定义如下属性:
  • byte[] buffer
    缓存区中数据容器。
  • int origin
    当前buffer中的读指针
  • int limit
    当前buffer的最大可读可写指针
  • int position
    当前buffer的写指针。
  • int semival


是否需要发送ACK(用于半同步)。
LogBuffer封装了字节相关的操作,不仅定义了上面的属性,也定义了字节读取相关众多API,其截图如下:

0edc977d39ed38c6fb4eaf54fa5f88b5.png

  • LogFetcher binlog日志抓取抽象类,定义了如下关键属性与抽象方法。
  • int DEFAULT_INITIAL_CAPACITY
    LogBuffer中的初始容量,默认为8K。
  • float DEFAULT_GROWTH_FACTOR
    容量增长因子,默认为 2.0。
  • int   BIN_LOG_HEADER_SIZE
    binlog日志条目 header 的长度,固定为4字节。
  • float  factor
    增长因子。
  • public abstract boolean fetch()
    抓取binlog日志。
  • public abstract void close()
        关闭 Fetch。


  • DirectLogFetcher Canal LogFetcher模式实现类,其核心属性如下:
  • SocketChannel channel
    网络通道,用于发送dump请求的网络通道。
  • boolean issemi = false
    是否开启半同步。


2.2 fetch流程详解


接下来我们重点剖析 DirectLogFetcher 的 fetch 方法,来探究其实现原理。


在研究DirectLogFetcher的fetch方法之前,我们先重点跟踪一下其内部网络读写方法fetch0方法,该方法是具体与网络读写相关的实现。

30c66e0fa15501b6c878102619d1eca5.png

在详细介绍该方法之前先来介绍一下其参数的含义:


  • int off
    从通道中读取到的内容放入到buffer中的起始位置
  • int len
    期望从通道中读取的字节长度。


该方法的实现关键点如下:


  • 首先先确保接收缓存区有足够的剩余空间,如果空间不足,则进行扩容。
  • 然后从通道中读取指定长度的字节。


接下来我们来重点看一下DirectLogFetcher的fetch的实现流程。

88b3041f7763a077a3449b3803b92f13.png

Step1:尝试从网络通道中读取4个字节(即读取协议的头部),如果通道中还没有可读取内容,返回false,造成的效果是一次 dump 请求结束。

01dceb224714c644dbf50effa7257690.png

Step2:从上文读到的4个字节分别读出该网络包的总长度以及当前包的序号,从这里可以看成MySQL协议头为4字节,前3个字节为网络包的总长度,第4个字节为包的序列号。再取出数据包的长度后,继续向通道中读取netlen个字节,即读取一个完整的数据包到buffer中。

31ac0f7a875ae9e9dce97f8740cf712f.png

Step3:继续从数据包中读取一个字节,判断该包的状态码,是否是一个成功的响应,如果是错误的响应,会向外抛出一次,Canal 会记录dump命令执行错误的次数。

4646eea6a112e278f7c74dd820aa55a1.png

Step4:如果一个包的长度为允许的最大包长度,则继续读取,这个主要是根据MySQL协议做的处理,即读取到一个数据包,然后返回true,表示拉取到一条日志,然后通过LogDecoder解码,然后传入到sink方法中,进行日志的后续处理。

13b9220d29fe9179d5cb86469eba150b.png

Step5:这一步的目的,就是将buffer中的当前指针指向数据的开始位置。这样一次 fetch就结束了。


从上面的流程来看,DirectLogFetcher#fetch 方法结束后,就将进入到LogDecoder中。经过一次DirectLogFetcher#fetch方法后,即取回一条binlog日志,即二进制流,接下来就根据binlog协议对其解析。本文暂不深入该方法,如果大家想深入数据库中间件方面,可以作为一个很好的示例,面向MySQL通信协议进行编程。


3、SinkFunction


通过 LogDecoder从中解析一个事件后,会调用SinkFunction的sink方法,如果该方法返回 false,一次dump请求将介绍,接下来我们看一下其sink方法。

8160a1a9b8a073c1de96d0a181adae27.png

该方法的实现比较简单,这里不打算继续深入,我们重点来看一下 Canal.Entry 的结构:

6c2135824a5326835195f4603001df4d.png

这个结构是基于Canal做架构设计,解决顺序消费、数据不丢失一个重要参考依据,没解析一条事务,最终放入到环形缓存区,环形缓存区尽量以一个事务提交到Sink组件,其代码如下:

89fec72644ac017e69a7836ea9abb69f.png

这里主要有如下几个关键点:


  • 首先需要调用EventSink组件将解析出来的数据传入EventSink。
  • EventSink组件处理成功后,会提交解析位点。


相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
27天前
|
SQL 存储 缓存
MySQL进阶突击系列(02)一条更新SQL执行过程 | 讲透undoLog、redoLog、binLog日志三宝
本文详细介绍了MySQL中update SQL执行过程涉及的undoLog、redoLog和binLog三种日志的作用及其工作原理,包括它们如何确保数据的一致性和完整性,以及在事务提交过程中各自的角色。同时,文章还探讨了这些日志在故障恢复中的重要性,强调了合理配置相关参数对于提高系统稳定性的必要性。
|
2月前
|
关系型数据库 MySQL 数据库
【赵渝强老师】MySQL的binlog日志文件
MySQL的binlog日志记录了所有对数据库的更改操作(不包括SELECT和SHOW),主要用于主从复制和数据恢复。binlog有三种模式,可通过设置binlog_format参数选择。示例展示了如何启用binlog、设置格式、查看日志文件及记录的信息。
165 6
|
3月前
|
SQL 存储 关系型数据库
美团面试:binlog、redo log、undo log的底层原理是什么?它们分别实现ACID的哪个特性?
老架构师尼恩在其读者交流群中分享了关于 MySQL 中 redo log、undo log 和 binlog 的面试题及其答案。这些问题涵盖了事务的 ACID 特性、日志的一致性问题、SQL 语句的执行流程等。尼恩详细解释了这些日志的作用、所在架构层级、日志形式、缓存机制以及写文件方式等内容。他还提供了多个面试题的详细解答,帮助读者系统化地掌握这些知识点,提升面试表现。此外,尼恩还推荐了《尼恩Java面试宝典PDF》和其他技术圣经系列PDF,帮助读者进一步巩固知识,实现“offer自由”。
美团面试:binlog、redo log、undo log的底层原理是什么?它们分别实现ACID的哪个特性?
|
3月前
|
存储 SQL 关系型数据库
面试官:你能聊聊 binlog、undo log、redo log 吗?
本文详细解析了MySQL数据库中的三种日志:binlog、undo log和redo log。binlog用于记录数据库的所有表结构变更及数据修改,支持归档、主从复制和数据恢复;undo log用于事务回滚,确保事务的原子性和实现多版本控制;redo log则用于crash-safe,确保数据库异常重启后已提交记录不丢失。文章通过实例和图表,深入浅出地介绍了每种日志的特点、应用场景及其实现机制。适合数据库开发者和运维人员阅读。
229 2
|
3月前
|
存储 关系型数据库 MySQL
MySQL中的Redo Log、Undo Log和Binlog:深入解析
【10月更文挑战第21天】在数据库管理系统中,日志是保障数据一致性和完整性的关键机制。MySQL作为一种广泛使用的关系型数据库管理系统,提供了多种日志类型来满足不同的需求。本文将详细介绍MySQL中的Redo Log、Undo Log和Binlog,从背景、业务场景、功能、底层实现原理、使用措施等方面进行详细分析,并通过Java代码示例展示如何与这些日志进行交互。
287 0
|
28天前
|
存储 SQL 关系型数据库
mysql 的ReLog和BinLog区别
MySQL中的重做日志和二进制日志是确保数据库稳定性和可靠性的关键组件。重做日志主要用于事务的持久性和原子性,通过记录数据页的物理修改信息来恢复未提交的事务;而二进制日志记录SQL语句的逻辑变化,支持数据复制、恢复和审计。两者在写入时机、存储方式及配置参数等方面存在显著差异。
|
4月前
|
canal 消息中间件 关系型数据库
Canal作为一款高效、可靠的数据同步工具,凭借其基于MySQL binlog的增量同步机制,在数据同步领域展现了强大的应用价值
【9月更文挑战第1天】Canal作为一款高效、可靠的数据同步工具,凭借其基于MySQL binlog的增量同步机制,在数据同步领域展现了强大的应用价值
872 4
|
5月前
|
SQL 关系型数据库 MySQL
【揭秘】MySQL binlog日志与GTID:如何让数据库备份恢复变得轻松简单?
【8月更文挑战第22天】MySQL的binlog日志记录数据变更,用于恢复、复制和点恢复;GTID为每笔事务分配唯一ID,简化复制和恢复流程。开启binlog和GTID后,可通过`mysqldump`进行逻辑备份,包含binlog位置信息,或用`xtrabackup`做物理备份。恢复时,使用`mysql`命令执行备份文件,或通过`innobackupex`恢复物理备份。GTID模式下的主从复制配置更简便。
578 2
|
5月前
|
SQL 关系型数据库 MySQL
【MySQL】根据binlog日志获取回滚sql的一个开发思路
【MySQL】根据binlog日志获取回滚sql的一个开发思路
|
11天前
|
SQL 关系型数据库 MySQL
数据库灾难应对:MySQL误删除数据的救赎之道,技巧get起来!之binlog
《数据库灾难应对:MySQL误删除数据的救赎之道,技巧get起来!之binlog》介绍了如何利用MySQL的二进制日志(Binlog)恢复误删除的数据。主要内容包括: 1. **启用二进制日志**:在`my.cnf`中配置`log-bin`并重启MySQL服务。 2. **查看二进制日志文件**:使用`SHOW VARIABLES LIKE 'log_%';`和`SHOW MASTER STATUS;`命令获取当前日志文件及位置。 3. **创建数据备份**:确保在恢复前已有备份,以防意外。 4. **导出二进制日志为SQL语句**:使用`mysqlbinlog`
53 2