简介:my2sql 是一款基于 Go 语言开发的 MySQL binlog 解析工具,面向需要做数据快速回滚、主从切换后数据修复、DML 行为分析以及大事务排查的 DBA 和运维工程师。它由 my2fback、binlog_rollback 等工具二次开发而来,除了支持常规数据类型外,还对 json、blob、text、emoji 等复杂类型做了 SQL 生成支持。压缩包共 153 个文件,以 Go 源码(119 个 .go)为主,辅以 md 文档、license 说明、mod 与 yml 工程配置,并附有可直接使用的 my2sql 可执行文件,整体大小约 4.36MB,便于快速部署和二次开发。资源包内还包含 binlog2sql 等同类工具的性能对比资料,已有 845 人下载学习。读者可获得完整的工具源码、项目结构与参数说明,据此掌握 binlog 解析、回滚 SQL 生成、DML 统计和大事务分析等实用技能,也可直接编译使用或按需定制,提升 MySQL 故障恢复与性能排查效率。
1. 项目概述与核心价值
做MySQL运维和数据恢复的同行,估计都经历过那种手一抖删错数据、或者线上一个update忘了加where条件的至暗时刻。平时调侃归调侃,真要碰上生产库误操作,第一反应基本都是在想有没有办法把数据捞回来。如果binlog是开着的,这事就有救——而救命的工具里,my2sql绝对算得上一个实用的选择。
my2sql是一个基于Go语言实现的MySQL binlog解析工具,核心用途就是读取binlog,把它还原成三类SQL:原始SQL、回滚SQL、以及去除主键的INSERT SQL;同时还能输出DML统计信息和大事务分析信息。也就是说,它既是一个“数据后悔药”制造器,也是一个审计和故障分析利器。适合数据库运维、后端开发、DBA相关人员使用,尤其是那些被线上误操作折腾过、想找个趁手工具做应急恢复和日常巡检的人。
这个工具最让我看重的一点,是它把binlog解析这件事做得很透。不是简单地把binlog事件翻译成SQL文本,而是对事务、主键、字段、行数据做了完整的结构化处理,所以才能在生成回滚SQL的时候做到“反着来”——DELETE变成INSERT,UPDATE反向赋值,逻辑非常干净。
1.1 为什么我需要一个binlog解析工具
MySQL的binlog是二进制格式,直接看是一堆乱码,没法直接拿来用。虽然MySQL官方提供了mysqlbinlog工具,能把binlog转成文本形式的SQL,但存在几个痛点:
- 生成的SQL带了大量注释和伪SQL(比如BEGIN、COMMIT、SET语句),有效信息混杂,人眼排查效率低。
- 无法直接生成回滚SQL。mysqlbinlog只能正向输出,你看到了那条误操作的DELETE,但它不会帮你生成对应的INSERT。
- 没有统计能力。想分析某个时间段内哪个表DML最多、哪个事务特别大,mysqlbinlog基本帮不上忙。
这些需求在实际生产中特别常见。我接过几个线上事故的排查:开发误删了一张配置表的数据、一个批量UPDATE把整列数据改错、某个从库延迟是因为一个超大事务……这些问题最后都是靠解析binlog定位和解决的。
1.2 my2sql的核心能力清单
在正式讲使用方法之前,先把my2sql能干什么列出来,方便大家对照自己的需求:
| 功能 | 说明 | 典型场景 |
|---|---|---|
| 生成原始SQL | 将binlog中的DML事件解析为可执行的原始SQL | 审计、回放、分析线上变更 |
| 生成回滚SQL | 将DML反向生成恢复SQL,DELETE转INSERT、UPDATE转反向UPDATE | 误操作数据恢复 |
| 无主键INSERT SQL | 生成不带主键字段的INSERT语句 | 数据迁移、异构同步 |
| DML统计信息 | 按表、按时间段统计INSERT/UPDATE/DELETE的行数 | 热点表分析、变更审计 |
| 大事务分析 | 识别超过指定阈值的大事务并输出详情 | 排查主从延迟、性能瓶颈 |
| 单条SQL分析 | 支持通过-sql参数过滤特定SQL类型 | 精确恢复某类操作 |
2. 工具选型与原理拆解
做binlog解析,市面上其实有几款工具,知名的有binlog2sql、mysqlbinlog_flashback、my2sql,还有基于Python的binlog_parse等。我在选型的时候重点对比了binlog2sql和my2sql,这里分享下我的判断。
2.1 为什么我最后选了my2sql
binlog2sql是大众点评开源的老牌工具,Python语言编写,支持生成回滚SQL和原始SQL,在GitHub上star很多。它本身没问题,但我用下来的体验是:
- 依赖Python环境,线上机器如果是个精简版系统,还得折腾依赖安装。
- 在超大binlog场景下,Python解析速度确实比不过编译型语言。
- 对binlog中event的处理是逐条翻译,输出结果会比较大,统计能力偏弱。
- 长时间解析时内存占用偏高,容易把运维机搞卡。
my2sql采用Go语言实现,编译后是单个二进制文件,扔到服务器上就能跑,不需要额外跑环境。解析性能实测比binlog2sql快不少,尤其在处理几个G的binlog文件时优势明显。加上它自带DML统计和大事务分析功能,等于一个工具干了多个活,所以最终我选定了my2sql作为主力工具。
2.2 my2sql的工作原理
my2sql有两种工作模式:实时解析(伪装成slave连接MySQL拉取binlog)和离线解析(直接扫描本地binlog文件)。原理上都是读取binlog中的事件流,关键处理环节有以下几步:
- 读取binlog文件头,解析format description event,获得binlog版本和事件格式。
- 逐条遍历binlog事件,识别TABLE_MAP_EVENT(表映射事件),将表ID和库名、表名关联起来。
- 识别WRITE_ROWS_EVENT、UPDATE_ROWS_EVENT、DELETE_ROWS_EVENT,这三类事件分别对应INSERT、UPDATE、DELETE操作。
- 通过binlog中的行镜像数据,结合表结构元信息,把二进制行数据还原成字段值。
- 在还原的基础上,根据用户参数决定是输出原始SQL、回滚SQL还是无主键INSERT SQL。
这个流程看着简单,实际实现有很多细节。比如binlog中行记录是变长编码的,解析时得按字段类型逐个解码;比如JSON类型字段在binlog里是二进制编码,处理起来比普通类型复杂得多;再比如遇到BLOB字段,还需要处理数据截断和转义问题。my2sql能把这些都处理好,说明代码质量是有保证的。
2.3 几个关键参数背后的设计逻辑
翻看my2sql的帮助文档,会发现它的参数设计非常有针对性。我挑几个印象深刻的说:
-mode参数:repl模式(实时拉取)和file模式(离线解析)二选一。repl模式适合线上实况分析,file模式适合事后悔查,二者互补。-start-file和-start-pos:指定从哪个binlog文件的哪个位置开始解析。这个设计很巧妙,因为MySQL的误操作往往有一个大致时间点,我们可以先用SHOW BINLOG EVENTS定位到具体位置,再精确解析,避免全量扫描浪费时间。-rollback参数:这是生成回滚SQL的总开关。一旦开启,my2sql会在解析过程中记录每行数据的变更前后值,并在输出时做反向转换。-big-tx参数:大事务阈值,单位是行数。设计这个参数的目的很直接——大事务会造成主从延迟,需要提前预警和排查。
3. 安装与快速上手
3.1 下载编译与环境准备
my2sql没有提供官方预编译二进制,需要自己用Go编译。不过编译过程很简单,前提是机器上有Go环境(1.16以上版本)。我按我的实际操作步骤写一下:
# 1. 克隆源码 git clone https://github.com/liuxin525/my2sql.git cd my2sql # 2. 设置临时代理(如果有网络问题的话,自己想办法) # 网络OK的直接执行: go mod tidy # 3. 编译 go build -o my2sql main.go # 4. 验证 ./my2sql --help编译好之后会得到一个my2sql可执行文件,直接扔到需要用的服务器上就能跑。这里提醒一点:需要依赖Linux系统的glibc库,如果目标机器是特别精简的容器镜像,可能需要用静态编译:
CGO_ENABLED=0 go build -a -ldflags '-s -w' -o my2sql main.go静态编译的好处是随便copy到哪台机器都能跑,不挑环境,建议在正式环境就这么干。
3.2 基础命令与参数速查
my2sql的参数不少,但我实际工作中常用的就那几个。整理成一张速查表:
| 参数 | 含义 | 示例 |
|---|---|---|
-mode | 解析模式:repl或file | -mode file |
-local | binlog所在目录(file模式必填) | -local /data/mysql/binlog |
-start-file | 起始binlog文件名 | -start-file mysql-bin.000123 |
-start-pos | 起始位置偏移量 | -start-pos 4 |
-stop-file | 结束binlog文件名 | -stop-file mysql-bin.000125 |
-stop-pos | 结束位置偏移量 | -stop-pos 1024 |
-start-datetime | 起始时间 | -start-datetime "2024-06-01 10:00:00" |
-stop-datetime | 结束时间 | -stop-datetime "2024-06-01 12:00:00" |
-sql | 过滤SQL类型,支持insert/update/delete | -sql insert |
-rollback | 生成回滚SQL | -rollback |
-big-tx | 大事务阈值(影响行数) | -big-tx 1000 |
-databases | 过滤指定库,逗号分隔 | -databases db1,db2 |
-tables | 过滤指定表,逗号分隔 | -tables user,order |
-output | 输出目录 | -output /tmp/recover |
-thread-count | 解析线程数 | -thread-count 8 |
提示:
-mode repl模式下,需要MySQL账号至少具备REPLICATION SLAVE和REPLICATION CLIENT权限;-mode file模式下只需要读binlog文件的权限。生产环境建议优先用file模式,不干扰线上服务。
4. 核心功能实操:SQL生成与回滚恢复
这一节是重点中的重点。我模拟一个常见场景:某天线上订单表的status字段被误更新,需要把数据恢复到更新前。我用my2sql走一遍完整流程。
4.1 生成原始SQL,看清线上到底执行了什么
首先,我找到涉及误操作的binlog文件和时间范围,用my2sql把原始SQL导出来:
mkdir -p /tmp/recover_raw /tmp/recover_rollback ./my2sql \ -mode file \ -local /data/mysql/binlog \ -start-file mysql-bin.000128 \ -start-datetime "2024-06-01 09:30:00" \ -stop-datetime "2024-06-01 10:30:00" \ -databases shop \ -tables orders \ -sql update \ -output /tmp/recover_raw执行完之后,在/tmp/recover_raw目录下会生成一个binlog_output.original文件。打开一看,之前那些神秘的binlog内容全被还原成了类似这样的SQL:
UPDATE `shop`.`orders` SET `id` = 1001, `status` = 2, `update_time` = '2024-06-01 09:35:10' WHERE `id` = 1001; UPDATE `shop`.`orders` SET `id` = 1002, `status` = 2, `update_time` = '2024-06-01 09:35:10' WHERE `id` = 1002;注意看,my2sql把WHERE条件也完整还原出来了。如果线上用的是刚提到的binlog_row_image=FULL,WHERE条件会包含所有字段的旧值,这对恢复定位来说非常关键。如果你的binlog设置的是MINIMAL格式,那WHERE条件里只有主键,但至少能定位到行。
4.2 生成回滚SQL,一键反悔
生成回滚SQL只需要加上-rollback参数:
./my2sql \ -mode file \ -local /data/mysql/binlog \ -start-file mysql-bin.000128 \ -start-datetime "2024-06-01 09:30:00" \ -stop-datetime "2024-06-01 10:30:00" \ -databases shop \ -tables orders \ -sql update \ -rollback \ -output /tmp/recover_rollback生成的回滚文件内容长这样:
UPDATE `shop`.`orders` SET `status` = 1, `update_time` = '2024-06-01 09:30:00' WHERE `id` = 1001; UPDATE `shop`.`orders` SET `status` = 1, `update_time` = '2024-06-01 09:30:00' WHERE `id` = 1002;对比一下原始SQL和回滚SQL,你会发现回滚SQL的SET部分是更新前的旧值,WHERE条件也是更新前的旧值,这保证了即使在并发环境下执行,也只会影响那一条原本被错误更新的事务涉及的行,不会误伤其他数据。这就是行级镜像解析带来的精度优势。
注意:生成回滚SQL之前,请务必检查binlog的行镜像格式。执行
SHOW VARIABLES LIKE 'binlog_row_image',如果值是MINIMAL,那UPDATE语句的WHERE条件只会包含主键,回滚精度稍弱;如果是FULL,则包含所有字段的旧值,更加安全可控。
4.3 去除主键的INSERT SQL,迁移和同步的好帮手
my2sql还有一个容易被忽略的功能:生成去除主键的INSERT语句。这是什么场景呢?我在做分库分表迁移或者数据归档的时候,经常需要把数据从一个环境同步到另一个环境,如果INSERT带上了主键,在目标环境可能会和自增主键冲突,或者触发主键覆盖,造成潜在问题。
比如,我想把binlog中的INSERT操作提取成不带主键的版本:
./my2sql \ -mode file \ -local /data/mysql/binlog \ -start-file mysql-bin.000128 \ -start-datetime "2024-06-01 09:30:00" \ -stop-datetime "2024-06-01 10:30:00" \ -databases shop \ -tables orders \ -sql insert \ -ignore-primary-key-for-insert \ -output /tmp/recover_insert生成的结果里,INSERT语句就不包含自增主键字段了:
INSERT INTO `shop`.`orders` (`product_id`, `user_id`, `status`, `create_time`) VALUES (501, 20001, 1, '2024-06-01 09:35:10');这个功能在话题迁移、灰度环境初始化、以及把binlog数据导入数仓这类场景下非常实用。
5. DML统计与大事务分析
除了SQL生成,my2sql还带了一个我很看重的功能——DML统计分析。它能让你快速了解一个时间段内,线上各表的读写分布情况,以及是否存在超大事务。
5.1 DML统计信息:哪个表变更最频繁一目了然
先看一个小案例。某天下午业务方反馈线上订单系统有延迟,我怀疑是某个时段有大量写入,用my2sql做了个统计:
./my2sql \ -mode file \ -local /data/mysql/binlog \ -start-file mysql-bin.000128 \ -start-datetime "2024-06-01 13:00:00" \ -stop-datetime "2024-06-01 14:00:00" \ -databases shop \ -output /tmp/analyse输出目录下会生成一个binlog_output.statistic文件,内容类似:
+---------------------+----------+--------+--------+--------+------------------+ | datetime | table | insert | update | delete | total | +---------------------+----------+--------+--------+--------+------------------+ | 2024-06-01 13:00:00 | shop.orders | 1200 | 3400 | 50 | 4650 | | 2024-06-01 13:00:00 | shop.payments | 800 | 300 | 20 | 1120 | | 2024-06-01 13:05:00 | shop.orders | 1500 | 4200 | 30 | 5730 | +---------------------+----------+--------+--------+--------+------------------+通过这个统计表,我很快定位到orders表在13:00到13:05之间出现了每分钟数千次的更新,结合业务排期发现是某个定时任务集中刷新订单状态导致的。如果不做binlog统计,这种写入洪峰很难从数据库表面指标上快速定位到具体表。
再补充一个实用技巧:-statistic参数后面如果设置一个目录,my2sql会把统计结果写入文件;如果不设置,默认输出到标准输出。在自动化脚本里,我一般设置目录输出,方便后续统一收集。
5.2 大事务排查:主从延迟的真凶
大事务是主从延迟的常见元凶。一个事务更新了几十万行,从库得单线程重放这部分数据,延迟瞬间拉满。my2sql专门提供了-big-tx参数来识别这类问题:
./my2sql \ -mode file \ -local /data/mysql/binlog \ -start-file mysql-bin.000128 \ -big-tx 5000 \ -output /tmp/analyse-big-tx 5000表示影响行数超过5000行的事务会被单独标记输出。分析结果会包含事务开始时间、涉及的表、影响行数、事务在binlog中的位置等信息。拿到这些信息后,就能针对性地去看那个时间点的代码,找出为什么会产生大事务。
我在实际排查中遇到过这样一个案例:某个“仅更新少量数据”的存储过程,因为循环里嵌套了查询,每次查询后更新一行,循环几千次,结果整个存储过程在binlog里变成一个巨型事务。用my2sql加上-big-tx扫一遍,几分钟就定位到了。如果没有这种统计能力,靠人肉翻binlog,真要看到眼瞎。
6. 常见问题与避坑指南
用了my2sql一段时间,也踩了不少坑。下面这些问题我基本都遇到过,写出来供大家参考。
6.1 典型问题与解决方案速查
| 问题现象 | 原因 | 解决方案 |
|---|---|---|
解析时报invalid event错误 | binlog文件不完整或格式异常 | 检查磁盘空间,确认binlog没有被截断;换一个完整的binlog文件试 |
| 生成的SQL出现乱码 | 连接MySQL时字符集配置不对 | 在命令中指定-charset utf8mb4,确保字符集一致 |
| 回滚SQL无法执行 | 目标表有外键约束或触发器 | 先禁用外键检查:SET FOREIGN_KEY_CHECKS=0,恢复后改回来 |
| 解析速度很慢 | 未指定-start-pos/-stop-pos,导致全文件扫描 | 先用SHOW BINLOG EVENTS定位精确位置,再做范围解析 |
| 解析出来的UPDATE语句特别长 | binlog_row_image设为FULL,WHERE条件包含全字段旧值 | 这是正常现象,无需处理;若想精简,可评估改MINIMAL的代价 |
-mode repl连接失败 | 账号权限不足或MySQL未开启binlog | 授权GRANT REPLICATION SLAVE, REPLICATION CLIENT;检查log_bin是否开启 |
6.2 实战中的几条经验教训
说几条我自己总结的经验吧。
第一,解析binlog之前,一定要先确认binlog的格式是ROW。my2sql只支持ROW格式的binlog,如果你的MySQL设置的是STATEMENT或MIXED格式,解析结果会不完整甚至出错。检查方法很简单:
SHOW VARIABLES LIKE 'binlog_format';如果设置的是ROW,那就没问题。生产环境强烈建议直接用ROW格式,这不仅是为了兼容my2sql,更重要的是ROW格式在数据恢复时能拿到行级变更前后的详细信息,恢复精度更高。
第二,恢复操作一定要在测试环境先演练一遍。我把回滚SQL从my2sql导出后,不会直接丢到生产库执行。而是先在测试库跑一遍,确认影响行数和预期一致,然后再上生产。如果生产库数据量大,回滚SQL建议分批执行,每批加个LIMIT或者分批提交,避免一次执行太久影响主库负载。
第三,备份永远是你的最后一道防线。my2sql能帮你恢复误操作的数据,但前提是binlog还在。如果binlog被清理了,或者binlog文件损坏了,再有用的工具也救不回来。所以每天定时备份、定期检查binlog保留策略,这些基本功不能丢。我个人的做法是,binlog至少保留3天,核心业务库保留7天,同时每天凌晨做全量备份,这样即使遇到极端情况,也能通过“全量备份+binlog增量”把数据恢复到任意时间点。
最后再分享一个小技巧:my2sql生成的SQL文件,如果数据量巨大(比如几万条UPDATE),直接用vim打开会卡顿。我一般会配合less命令翻页查看,或者用grep配合行号定位关键SQL:
# 查看总行数 wc -l binlog_output.original # 定位到某个关键词附近 grep -n "orders" binlog_output.original | head -20 # 查看某个区间的内容 sed -n '100,120p' binlog_output.original这些简单的文本处理命令,配合my2sql的解析结果,排查问题效率会高很多。
本文还有配套的精品资源,点击获取