Flink CDC读Binlog导致MySQL主库IO飙高——怎么优化不影响线上?
本文目录
先说结论
源库压力来自三处:① 全量snapshot时的SELECT扫表(最重);② Binlog dump线程持续读binlog文件;③ 元数据查询(SHOW CREATE TABLE等)。
最有效的三招:① 走只读从库不连主库;② 全量snapshot分chunk小并发慢慢跑;③ Binlog dump走专门的复制账号+限速。
终极方案:用专门的Binlog采集中转服务(比如Canal)做共享,避免每个CDC作业都直连源库。
先把这段讲顺;面试官追问时,再展开下面的原理和细节。
详细讲解
CDC采集对源库的压力是DBA最关心的事情,没有之一。第一次接Flink CDC时,DBA第一反应就是"你们别把主库搞挂了"。这道题面试官不光要听你怎么优化,还要听你懂不懂源库的运行机制——为什么会高、高在哪里、怎么测、怎么降。
业务场景:DBA凌晨告警事件
接入新业务时,启动了一个Flink CDC作业同步orders表(约5亿行)。凌晨2点启动,3点DBA电话报警:MySQL主库IO util 95%,binlog磁盘IO飙到400MB/s,业务慢查询激增。复盘发现:
故障复盘时间线:
02:00 Flink CDC作业启动,scan.startup.mode=initial
02:00 启动snapshot:8并发chunk,每chunk扫表
02:15 IO util从30%涨到80%(snapshot还没完)
02:30 Buffer Pool命中率从99%掉到92%(snapshot把热数据冲掉)
03:00 IO util 95%,业务接口p99从100ms涨到800ms
03:05 DBA强制kill掉Flink CDC的session
教训:
- snapshot连了主库
- 并发太大没节流
- 没跟DBA同步评估窗口
方案一:走只读从库 + 复制账号隔离
最重要的一条规则:CDC永远不连主库。专门搭只读从库给数据团队用,权限和连接都跟业务隔离。
-- 1. 在只读从库上建专用CDC账号
CREATE USER 'flink_cdc'@'%' IDENTIFIED BY 'xxx';
GRANT SELECT, RELOAD, SHOW DATABASES, REPLICATION SLAVE, REPLICATION CLIENT
ON *.* TO 'flink_cdc'@'%';
FLUSH PRIVILEGES;
-- 2. MySQL限制此账号的连接数和资源
ALTER USER 'flink_cdc'@'%' WITH MAX_USER_CONNECTIONS 20;
-- 3. Flink CDC配置只连readonly slave
'hostname' = 'mysql-cdc-readonly.internal' -- 专门的从库域名
'username' = 'flink_cdc'
-- 4. 给从库配额外的硬件,跟业务从库分开
-- (业务从库给后端服务读,CDC从库给数据团队用)
适用场景:生产环境必须;中大型公司有DBA团队的标配。
注意:从库有复制延迟,如果业务对秒级一致性敏感(比如订单状态变化要立刻反映到风控),需要特别评估这个延迟能否接受。一般延迟在1-3秒内。
方案二:snapshot限流 + chunk配置
Flink CDC 2.x的snapshot算法虽然无锁,但并发太大照样把磁盘IO打满。关键参数有三个,必须根据表大小和源库IO配置仔细调。
CREATE TABLE orders_cdc (...) WITH (
'connector' = 'mysql-cdc',
'hostname' = 'mysql-readonly',
-- chunk大小:默认8096,大表调到32000-50000减少chunk数量
'scan.incremental.snapshot.chunk.size' = '50000',
-- 并发snapshot reader:默认1(顺序扫),多并发用server-id范围
'server-id' = '5400-5403', -- 4个并发reader
-- chunk key列:必须有索引(一般是主键)
'scan.incremental.snapshot.chunk.key-column' = 'id',
-- 关键:snapshot fetch大小(一次fetch多少行)
'scan.snapshot.fetch.size' = '1024', -- 默认1024,IO敏感时调小到256
-- 是否在snapshot时连续扫binlog(2.4+)
'scan.incremental.close-idle-reader.enabled' = 'true'
);
-- Flink作业层限流:source的并行度=server-id范围
SET 'parallelism.default' = '4';
-- 限制snapshot吞吐(自定义,通过RateLimiter算子)
适用场景:大表初始化(千万行以上);源库性能敏感。
不适用:小表(几十万行),多并发反而是overhead。
方案三:用Canal/中转层共享Binlog流
如果有10个Flink CDC作业都要消费同一个MySQL实例,每个都直接dump binlog,等于10倍的binlog读IO。最佳实践是用Canal统一拉一份Binlog到Kafka,所有下游消费方从Kafka消费。
❌ 每个CDC作业直连(N个作业=N倍binlog IO):
MySQL ←──── Flink CDC Job 1 (订单实时大盘)
←──── Flink CDC Job 2 (用户画像更新)
←──── Flink CDC Job 3 (风控特征)
←──── ...10个作业
✓ 共享Binlog层(只1份binlog IO):
MySQL ←─ Canal/Debezium ─→ Kafka
├─→ Flink Job 1
├─→ Flink Job 2
├─→ Flink Job 3
└─→ ...任意多个
Kafka做缓冲层还有额外好处:① 多消费方互相不影响;② 下游作业重启不影响源库;③ 历史回溯方便(Kafka保留3-7天)。
方案对比
| 维度 | 直连主库 | 走只读从库 | Canal+Kafka中转 |
|---|---|---|---|
| 源库压力 | 极高 | 中(从库) | 极低(只1份) |
| 延迟 | 最低(<1s) | +复制延迟(1-3s) | +Kafka延迟(2-5s) |
| 扩展性 | 差 | 中 | 极好 |
| 运维成本 | 低 | 低 | 高(多组件) |
| 适用规模 | 禁止生产 | <5个CDC作业 | >5个CDC作业 |
面试高频追问
追问一:Flink CDC的snapshot会不会锁表?
1.x版本会用FLUSH TABLES WITH READ LOCK锁全库(生产严禁)。2.x引入Chunk-based算法,借鉴Netflix DBLog论文:把表按主键切chunk,每个chunk内部用"select+binlog对齐"的方式快照,整个过程不需要任何锁。这是Flink CDC 2.x能在生产大规模落地的关键。
追问二:怎么测算CDC对源库的真实压力?
一般做三组对比:① 启动CDC前后对比QPS、IOPS、Buffer Pool命中率;② 用sysbench模拟业务负载,CDC启动前后对比tpmC;③ 监控slow query log,看慢查询数量变化。如果Buffer Pool命中率掉了2%以上,或者业务p99上涨了20%以上,就要立刻调整。
追问三:snapshot跑了一半作业挂了,重启会从头来吗?
不会,这正是Chunk-based算法的优势。每个chunk完成后会触发Flink的Checkpoint,记录已完成的chunk列表。作业重启时从Checkpoint恢复,已完成的chunk跳过,未完成的chunk重新跑。生产上一定要把checkpoint间隔配合理(snapshot时建议5分钟一次),不然挂了重启代价太大。
实战踩坑:Buffer Pool被snapshot冲掉导致业务慢查询
还有一个隐藏坑:snapshot会大量SELECT全表,把InnoDB Buffer Pool里的业务热数据冲掉。业务SQL本来都走Buffer Pool(命中率99%),snapshot一跑命中率掉到80%,所有业务查询都变成磁盘IO,p99暴涨。这个比直接IO高更致命,因为问题持续到snapshot结束很久。
-- 监控Buffer Pool命中率
SELECT
ROUND((1 - (Innodb_buffer_pool_reads / Innodb_buffer_pool_read_requests)) * 100, 2)
AS hit_rate_pct
FROM (SELECT VARIABLE_VALUE AS Innodb_buffer_pool_reads
FROM performance_schema.global_status
WHERE VARIABLE_NAME = 'Innodb_buffer_pool_reads') a,
(SELECT VARIABLE_VALUE AS Innodb_buffer_pool_read_requests
FROM performance_schema.global_status
WHERE VARIABLE_NAME = 'Innodb_buffer_pool_read_requests') b;
-- 解决:在专门的从库上跑snapshot,业务从库不受影响
-- 同时snapshot时段尽量选业务低峰(凌晨2-5点)
-- snapshot结束后业务从库的Buffer Pool不会被冲
解决方案:后来把CDC从库做了物理隔离,专门给数据团队用,同时大表snapshot强制选业务低峰。从那之后再没收到DBA的电话告警。
总结
CDC对源库的影响要从"全量snapshot+持续binlog dump+元数据查询"三方面看。最佳实践三件套:走只读从库、合理配snapshot chunk参数、多消费方用Canal+Kafka共享。面试时把"为什么会高+怎么测+怎么降"讲清楚,比单纯背参数分高得多——DBA和数据团队的协作能力是这道题的隐藏考点。
下一篇聊埋点采集的痛点:埋点上报数据丢了30%,前端SDK的可靠性怎么保证?
你们公司CDC作业是直连主库还是走从库?踩过DBA的雷吗?评论区聊聊~
关注「Wood聊大数据」| 9年大数据老兵,聊技术、聊职场、聊成长
数据采集 面试通关手册
第6-10篇:埋点与日志采集体系
Wood聊大数据
9年大数据老兵,聊技术、聊职场、聊成长