Binlog 中不完整的 Event 排查

本文探讨了在高并发机房容灾切换中遇到的两个关键问题:sql_thread停止异常和slavestatus位点不一致。这些问题源于未完整消费的半个事务,通过限流和binlog分析揭示了coordinate线程挂起的根本原因,并给出了针对性的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

容灾切换中的老鼠屎

机房级别容灾的场景下,单次的 db 切换数量高达 2000+,难免遭遇各种各样平时难以遇到的问题。例如,下面这两个问题就困扰了我们一段时间:

  • 第一个问题是:sql_thread 无法正常 stop,导致切换失败
    在进行主备切换的时候,为了避免 sql_thread 写入和业务写入冲突,首先会 stop slave sql_slave。但是在 stop slave 的时候,有很小的概率会执行超时。

  • 第二个问题是:slave status 中的位点无法对齐,导致切换速度慢
    在进行主备切换之前,会等待一段时间,让新主库把拉取到的 relaylog 消费掉,等待过程中会根据 slave status 中的位点状态来判断 relaylog 是否消费完成:
    但是有很小的概率会遇到这样的情况:无论等待多长时间,mlf.rmpl 和 rmlf.emlp 都不会对齐。

Name	Value	Discription
Master_Log_File(mlf)	mysql-bin.000106	io_thread 正在读取的 master 的 binlog 文件
Read_Master_Log_Pos(rmlp)	41956375	io_thread 读取到的 master 的 binlog 的位点
Relay_Master_Log_File(rmlf)	mysql-bin.000106	sql_thread 最新执行完成的 event 所在 master 的 binlog 文件
Exec_Master_Log_Pos(emlp)	41956375	sql_thread 最新执行完成的 event 所在 master 的 binlog 的位点
问题的定位和重现

根据切换日志中记录的 mlf.rmpl 和 rmlf.emlp,通过解析 binlog 发现,没有被消费的 binlog 其实是一个不完整的事务,没有 COMMIT(Xid EVENT):

Name	Value
Master_Log_File	mysql-bin.000106
Read_Master_Log_Pos	80338810
Relay_Master_Log_File	mysql-bin.000106
Exec_Master_Log_Pos	80322348

为了复现备库接收到半个事务的场景,通过在主库进行端口限流来控制 binlog 的 dump 速度,从而重现问题

shell 
# tc qdisc add dev eth0 root handle 1: htb default 30 
# tc class add dev eth0 parent 1: classid 1:1 htb rate 8kbit burst 15k 
# tc filter add dev eth0 protocol ip parent 1:0 prio 1 u32 match ip sport 3307 0xffff flowid 1:1 

问题重现后不难发现,问题一和问题二的触发原因是相同的,binlog 中存在半个事务。
问题已发生的根本原因是,coordinate 线程 hang 住了。
解决方案

slave status 中的位点无法对齐,导致切换速度慢
rmlf.emlp 在 20s 内无任何变化时,认为 relaylog 执行到终点,进行切换的下一步。
sqlthread 无法正常 stop,导致切换失败
stop slave 超时后,kill 掉 replication 的 worker 线程。
MTS 工作原理介绍
  • worker 线程和 sql 线程一样,会直接执行拿到的 event
  • 当 stop slave 或 kill thread,没有 commit 的事务会被回滚
### MySQL Binlog 的概念、作用与原理 #### 什么是 BinlogBinlogMySQL 数据库中的二进制日志文件,主要用于记录数据库中发生的 **数据变更事件**。这些事件包括但限于 `INSERT`、`UPDATE` 和 `DELETE` 等操作[^1]。需要注意的是,Binlog 记录的内容并是表结构或索引的具体内容,而是描述了哪些操作引起了数据的变化。 --- #### Binlog 的作用 1. **主从复制 (Master-Slave Replication)** Binlog 是实现 MySQL 主从复制的核心机制之一。当 Master 节点上的数据发生变化时,其 Binlog 文件会被 Slave 节点读取并重放,从而保持两者的数据一致性[^3]。 2. **数据恢复** 如果数据库发生了意外崩溃或其他问题,可以通过 Binlog 进行增量恢复。通过解析 Binlog 文件,可以重新应用那些未完成的事务,使数据回到最新的状态[^4]。 3. **审计功能** Binlog 提供了一种方式来追踪数据库的历史更改记录,这对于排查错误或者分析历史行为非常有用。 --- #### Binlog 的工作原理 MySQL 支持三种同的 Binlog 格式,每种格式都有自己的特点: 1. **Statement Format(语句级日志)** - 记录的是每个执行的 SQL 语句本身。 - 缺点在于某些复杂查询可能无法完全重现相同的结果集,尤其是在涉及随机数函数或时间戳的情况下[^1]。 2. **Row Format(行级日志)** - 记录的是每一行数据的具体变化情况。 - 行级日志的优点是可以精确地反映每一行数据的修改细节,尤其适合于跨版本兼容性和高精度需求场景[^4]。 3. **Mixed Format(混合日志)** - 结合了 Statement 和 Row 两种模式的优势,在必要时动态切换日志记录的方式。 - 对于大多数情况下,默认采用 Statement 模式;而对于一些特殊语句,则会自动切换至 Row 模式以确保准确性[^1]。 --- #### 实现案例:基于 Row Level 的优势 在 Row Level 模式的 Binlog 中,由于只关注具体的行级别改动而关心上下文信息,因此能够更清晰地展示每次数据变动的细节。这种特性使得它特别适用于需要高度一致性的环境,比如分布式系统之间的同步[^4]。 ```python # 示例 Python 解析 Row-Level Binlog 工具伪代码 def parse_row_level_binlog(binlog_file): with open(binlog_file, 'rb') as f: while True: event = read_next_event(f) # 假设这是一个方法用来读取下一个事件 if not event: break process_event(event) def process_event(event): if event.type == 'ROW_UPDATE': print(f"Updated row: {event.row_data}") elif event.type == 'ROW_INSERT': print(f"Inserted new row: {event.row_data}") elif event.type == 'ROW_DELETE': print(f"Deleted row: {event.row_data}") parse_row_level_binlog('example.bin') ``` --- #### 总结 BinlogMySQL 生态系统中扮演着至关重要的角色,无论是支持高效的主从复制还是提供可靠的数据恢复手段,都离开它的贡献。通过对 Binlog 日志的同格式选择,用户可以根据实际业务需求灵活调整性能与安全之间的平衡。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值