Cdc: 如何为 mysql 做快照
Debezium 是 Kafka Connect 生态体系下的一个 source connector, 支持实时将 MySQL, PostgreSQL 等数据库的变更事件转化为 Kafka 的消息. 随后, 我们可以通过种类丰富的 sink 将数据导入其他数据源.
本文以 MySQL 为例, 简单介绍 Debezium 在初次创建 connector 时为数据库做快照的策略.
- 获取读锁, 限制表结构的变更
- 读取表结构
- 设置事务隔离级别为 REPEATABLE READ
- 释放读锁, 允许对表的结构和数据进行更新
- 读取所有表的数据
锁
Debezium 优先会选择全局读锁来限制表结构的变更,
对应 SQL FLUSH TABLES WITH READ LOCK
.
在无法获取全局读锁的情况下, Debezium 需要为每一张表单独加锁,
对应 SQL FLUSH TABLE {} WITH READ LOCK
.
优先使用全局读锁而非表锁的主要原因在于:
- Debezium 通过设置事务隔离级别为 RR 来保证全表数据的一致性, 读取到的都是在事务开始那一刻的快照
- 释放表锁会同时提交当前事务, 所以在读取完全部数据前需要持有锁, 视数据规模, 写禁止会持续几分钟到几十分钟
- 释放全局读锁并不会导致事务被提交, 所以可以在读取完表结构后即释放锁, 写禁止的时间一般小于一秒, 和数据规模无关
扫表
这个操作其实在业务上也会比较常见, 一些人会错误的通过不断更新 OFFSET 来实现扫全表. 在稍有规模的数据集就会发现当 OFFSET 变大时, SQL 执行的很慢. 于是更换成浮标式的分页, 即使用当前查询的主键做为下次查询的限制条件, 规避对 OFFSET 的需求.
Debezium 利用 JDBC 提供的 stream 概念简化了逻辑的同时, 提高了效率.
从使用上看, 通过设置 TYPE_FORWARD_ONLY
和 CONCUR_READ_ONLY
, JDBC 会实现类似 lazy load 的效果.
先加载 n
条数据, 当用户需要 n+1th 数据时再加载 n
条数据.
虽然没有去看 JDBC 具体的实现, 但是结合文档中提及的内容, 我们可以猜测:
- JDBC 向 server 原封不动的发送了 sql.
- socket 读取到 n 条数据后, hang 住直到上游需要更多的数据.