MySQL master 将数据变更写入二进制日志( binary log, 其中记录叫做二进制日志事件binary log events,可以通过 show binlog events 进行查看)MySQL slave 将 master 的 binary log events 拷贝到它的中继日志(relay log)MySQL slave 重放 relay log 中事件,将数据变更为它自己的数据
canal 模拟 MySQL slave 的交互协议,伪装自己为 MySQL slave ,向 MySQL master 发送 dump 协议MySQL master 收到 dump 请求,开始推送 binary log 给 slave (即 canal )canal 解析 binary log 对象(原始为 byte 流)
canal [kə’næl],译意为水道/管道/沟渠,主要用途是基于 MySQL 数据库增量日志解析,提供增量数据订阅和消费目前canal只支持mysql和本地binlong文件解析
Binlog发送接收流程,流程如下图所示:
该消息是备份连接时由从服务器向主服务器发送的最后一个请求,主服务器收到后,会响应一系列的报文,
每个报文都包含一个二进制日志事件。如果主服务器出现故障时,会发送一个EOF报文
备注:大字节序是高位数据存储在内存高位地址,低位数据存储在低位地址,小字节序是高位数据存储在内存低位地址,低位数据存储在高位地址
// 0. write command number (byte) 0x12
// 1. write 4 bytes bin-log position to start at
// 2. write 2 bytes bin-log flags
// 3. write 4 bytes server id of the slave
// 4. write bin-log file name
dump消息发送成功后,MySQL主服务器已经接受了 canal 这个从服务器,那么canal之后的工作就是解析拿到的binlog内容解析之前必须要把binlog-format设置为ROW模式,canal是基于行的复制的。binlog中每一个数据变更可以叫做事件,在ROW模式下,有几个主要的事件类型:
每一次数据的变更,都会触发2个事件,先把要更改的表信息告诉你,然后再告诉你更改的row内容,比如TABLE_MAP_EVENT + WRITE_ROWS_EVENT
说明:
server代表一个canal运行实例,对应于一个jvm,instance对应于一个数据队列 (1个server对应1…n个instance)
instance模块:
eventParser (数据源接入,模拟slave协议和master进行交互,协议解析)
eventSink (Parser和Store链接器,进行数据过滤,加工,分发的工作)
eventStore (数据存储)
metaManager (增量订阅&消费信息管理器)
目前canal支持所有模式的增量订阅(但配合同步时,因为statement只有sql,没有数据,无法获取原始的变更日志,所以一般建议为ROW模式)
All events have a common general structure consisting of an event header followed by event data:
Sink阶段所做的事情,就是根据一定的规则,对binlog数据进行一定的过滤一般有两次过滤,分别是:
根据表名、库名等进行过滤,通过AviaterRegexFilter,根据库名和表名称进行过滤主要内容是HEARTBEAT类型的事件表名、库名过滤:
1、目前仅实现了Memory内存模式,后续计划增加本地file存储,mixed混合模式
2、借鉴了Disruptor的RingBuffer的实现思路,RingBuffer设计:
实现说明:
Put/Get/Ack cursor用于递增,采用long型存储(AtomicLong)
buffer的get操作,通过取余或者与操作。(与操作:cusor & (size - 1) , size需要为2的指数,效率比较高)
indexMask = size-1
Put 操作:添加数据。event parser模块拉取到binlog后,并经过event sink模块过滤,最终就通过Put操作存储到了队列中。
Get操作:获取数据。canal client连接到canal server后,最终获取到的binlog都是从这个队列中取得。
Ack操作:确认消费成功。canal client获取到binlog事件消费后,需要进行Ack。你可以认为Ack操作实际上就是将消费成功的事件从队列中删除,如果一直不Ack的话,队
列满了之后,Put操作就无法添加新的数据了。对应的,我们需要使用3个变量来记录Put、Get、Ack这三个操作的位置,其中:
putSequence: 每放入一个数据putSequence +1,可表示存储数据存储的总数量
getSequence: 每获取一个数据getSequence +1,可表示数据订阅获取的最后一次提取位置
ackSequence: 每确认一个数据ackSequence + 1,可表示数据最后一次消费成功位置
另外,putSequence、getSequence、ackSequence这3个变量初始值都是-1,且都是递增的,均用long型表示。由于数据只有被Put进来后,才能进行Get;Get之后才能进行Ack。所以,这三个变量满足以下关系:
ackSequence <= getSequence <= putSequence