4、主从复制原理:MySQL Binlog解析、异步复制与半同步复制、复制延迟问题
各位同学,今天我们来聊聊MySQL主从复制。说实话,这是我在生产环境中打交道最多的模块之一。很多刚入行的朋友觉得复制很简单——不就是主库写,从库同步嘛。但真到了线上,你会发现坑多得很。
我见过不少团队,因为复制延迟导致数据不一致,最后不得不全量重建从库。也见过因为Binlog格式选错,导致数据恢复时发现少了一截。嗯,今天我们就把这些东西掰开揉碎了讲清楚。
4.1 Binlog:复制的基石
Binlog,全称Binary Log,二进制日志。说白了,它就是MySQL记录所有数据变更的“账本”。主从复制能跑起来,全靠这个日志。
我个人习惯把Binlog理解成“数据库的录像带”。你做的每一次INSERT、UPDATE、DELETE,甚至表结构变更,都会被录下来。从库拿到这盘“录像带”,从头到尾播放一遍,数据就同步了。
Binlog有三种格式,我列个表给你看:
| 格式 | 说明 | 我踩过的坑 |
|---|---|---|
| STATEMENT | 记录SQL语句本身 | 曾经因为UUID()函数导致主从数据不一致 |
| ROW | 记录每一行数据的变化 | 日志量巨大,但最安全 |
| MIXED | 混合模式,MySQL自动选择 | 某些场景下会退化成STATEMENT |
我在项目中遇到过一个问题:主库执行了DELETE FROM t WHERE id > 100 LIMIT 1,STATEMENT格式下,从库可能删掉的是另一条记录。为什么?因为LIMIT 1没有ORDER BY,主从库的数据分布可能不同。从那以后,我所有生产环境都强制使用ROW格式。
核心建议:生产环境请使用ROW格式。虽然日志量大,但数据一致性有保障。磁盘便宜,数据丢了可就贵了。
4.2 异步复制:默认但不够可靠
MySQL默认的复制模式就是异步复制。流程很简单:
- 主库提交事务,写入Binlog
- 主库直接返回客户端“成功”
- 从库通过IO线程拉取Binlog
- 从库通过SQL线程回放日志
你想想看,这里有个问题:主库返回成功的时候,从库可能还没拿到数据。如果这时候主库宕机了,数据就丢了。我见过一个案例,某电商平台凌晨做活动,主库突然挂了,丢了大概3秒的数据,结果订单对不上,财务加班对账对了一整天。
异步复制的优点很明显——性能好。主库不用等从库确认,写操作延迟很低。但缺点也很致命——数据可能丢。说白了,这就是用可靠性换性能。
我的经验:如果业务允许丢少量数据(比如日志、统计信息),异步复制完全够用。但如果是金融、订单类业务,千万别这么干。
4.3 半同步复制:折中的方案
半同步复制(Semi-Synchronous Replication)就是为了解决异步复制丢数据的问题而生的。它的核心逻辑是:主库提交事务后,必须等待至少一个从库确认收到了Binlog,才返回客户端成功。
流程变成了这样:
- 主库提交事务,写入Binlog
- 主库等待从库确认(默认超时10秒)
- 从库收到Binlog,写入Relay Log,返回ACK
- 主库收到ACK,返回客户端成功
这里有个细节要注意:半同步复制不是“同步复制”。从库只是确认收到了Binlog,并不保证已经回放完成。所以从库仍然可能有延迟,但至少数据不会丢。
我曾经在一个支付系统里配置了半同步复制。配置命令很简单:
-- 主库安装插件
INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';
-- 从库安装插件
INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so';
-- 主库开启
SET GLOBAL rpl_semi_sync_master_enabled = 1;
SET GLOBAL rpl_semi_sync_master_timeout = 10000; -- 超时10秒
-- 从库开启
SET GLOBAL rpl_semi_sync_slave_enabled = 1;
注意:半同步复制有超时机制。如果从库10秒内没确认,MySQL会自动降级为异步复制。我曾经遇到过网络抖动导致频繁降级,后来把超时时间调到了30秒才稳定下来。
4.4 复制延迟:绕不开的痛
复制延迟,说白了就是从库比主库“慢半拍”。我见过最夸张的延迟是几个小时——一个数据分析团队在从库上跑了个大查询,直接把从库的SQL线程卡死了。
延迟的原因有很多,我列几个常见的:
- 主库写入压力大:主库每秒几万条写入,从库来不及回放
- 从库配置低:CPU、磁盘IO跟不上主库
- 大事务:一个事务改了1000万行,从库要慢慢回放
- 从库上有查询:慢查询会阻塞SQL线程
- 网络延迟:主从库跨机房,带宽不够
怎么监控延迟?最常用的命令是SHOW SLAVE STATUS\G,重点关注两个字段:
Seconds_Behind_Master: 0 -- 延迟秒数
Master_Log_File: mysql-bin.000123
Read_Master_Log_Pos: 456789
Relay_Master_Log_File: mysql-bin.000123
Exec_Master_Log_Pos: 456789
如果Seconds_Behind_Master一直大于0,说明有延迟。但要注意,这个值不一定准确——如果主从库的时钟不同步,这个值就是错的。我习惯同时对比Master_Log_File和Relay_Master_Log_File,看从库追到了哪个位置。
避坑指南:我曾经遇到一个案例,Seconds_Behind_Master显示为0,但数据就是不一致。后来发现是因为从库的SQL线程卡住了,但IO线程还在正常拉取Binlog。所以只看延迟秒数是不够的,要结合Slave_SQL_Running_State一起看。
4.5 知识体系总览
下面这张图是我自己画的,把主从复制的核心逻辑串起来了。你看一眼,应该能对整个流程有个直观的理解。
这张图把主从复制的核心流程都画出来了。你看,主库的Binlog通过网络传到从库的IO线程,IO线程写入Relay Log,然后SQL线程从Relay Log里读出来回放到从库的InnoDB里。半同步复制就是在主库和从库之间多了一条ACK确认线。
好了,关于主从复制的原理,我们就聊到这里。Binlog格式选ROW,半同步复制保数据,延迟监控要全面——这三句话记住了,线上能少踩很多坑。
公众号:蓝海资料掘金营,微信deep3321