1. 介绍

canal是阿里开源的一个binlog解析工具。本文对其设计上的一些小细节进行一番琢磨。

如果还不熟悉canal可以看看canal官方wiki和我写的canal源码解析系列。然后再看我现在这篇文章,可能会有更多的共鸣。

2. binlog解析原理的小思考

按照wiki所说,从上层来看,复制分成三步:

  1. master将改变记录到二进制日志(binary log)中(这些记录叫做二进制日志事件,binary log events,可以通过show binlog events进行查看);
  2. slave将master的binary log events拷贝到它的中继日志(relay log);
  3. slave重做中继日志中的事件,将改变反映它自己的数据。

这里可以问几个问题: slave的二进制日志事件是怎么被拷贝到slave的中继日志中的?
master端有个dump thread负责监听binary log event,如果有新的事件了则通知slave的slave I/O thread。这个slave I/O thread会负责读取dump thread的通知信息来拷贝新的binary log event.

注意以下重点:

  1. 操作事务提交之前写入binlog记录
  2. master和slave通过dump thread和slave I/O thread来通信

参考资料:

  1. 高性能Mysql主从架构的复制原理及配置
  2. mysql官方文档-Replication Implementation Details

3. EventStore为啥设计成RingBuffer

优点:

  1. FIFO的情况下,消费掉数据之后不用移动其他元素。相比其他数据结构来说,这种数据结构在数据组织上比较“稳定”
  2. 内存利用率高,因为环形的嘛。不需要分配额外内存。
  3. 实现简单高效,要做成线程安全的也很简单。把offset用原子对象来控制就好了。

缺点:

  1. 缓存容量需要比较固定,不会经常变动。否则使用ring buffer经常调节容量,数据移动多。也就是意味着一开始分配多大的buffer size需要好好设计。size建议2的倍数

4. canal如何保证有序性

在谈论MQ的时候我们经常会谈论消息有序性。其实要做有序性的方式很简单:即保证一个生产者只对应一个消费者。

显然canal也是这么做的。canal client: 为了保证有序性,一份instance同一时间只能由一个canal client进行get/ack/rollback操作,否则客户端接收无法保证有序。

看看get/ack的过程:

  1. 每次get操作都会在meta中产生一个mark,mark标记会递增,保证运行过程中mark的唯一性
  2. 每次的get操作,都会在上一次的mark操作记录的cursor继续往后取,如果mark不存在,则在last ack cursor继续往后取
  3. 进行ack时,需要按照mark的顺序进行数序ack,不能跳跃ack. ack会删除当前的mark标记,并将对应的mark位置更新为last ack cursor
  4. 一旦出现异常情况,客户端可发起rollback情况,重新置位:删除所有的mark, 清理get请求位置,下次请求会从last ack cursor继续往后取

5. canal为啥同一时间只能一个instance运行,其他standby

wiki里面说是为了减少对mysql dump的请求。这个应该是mysql5.7的master dump thread不能并发读binlog event造成的。因此即使有更多的instance同时执行,应该也不能大幅提升解析效率,反而引入更多的竞争。

mysql5.8对master dump thread增加了一些改进,详情见:

  1. MySQL5.7新特性(官方文档中文翻译)
  2. What Is New in MySQL 5.7

如何保证只有一个instance运行? 通过抢占ZK临时节点。canal client也通过这种方式保证一份instance同一时间只能由一个canal client进行get/ack/rollback操作。

6. canal的高可用设计

instance有几种配置。支持高可用的方式就是把parser、sink、store都做成无状态的。数据存储在ZK来保证高可用。

7 get/ack异步化的理解

get/ack做了异步化。这里的异步化指的是:client通过get收到数据后马上返回ack,然后继续get,不会阻塞。如果后面需要rollback了,可能server那边还没有接收到所有的ack更新位点信息。所以整个过程可以理解成是异步的。之所以这样设计是考虑rollback可能性比较小。如果rollback无非就是要多重复消费一些位点了。

参考资料:

  1. What are the uses cases of ring buffer?
  2. What are the uses of circular buffer?