常见报错“failed to open file”主因是系统用户权限不足或selinux/apparmor拦截,须用绝对路径并确保读权限;默认输出为二进制格式,需加--base64-output=decode-rows和-v才可读sql;row格式下无法还原原始sql,仅显示行变更;大文件应按时间或位置截断解析,避免内存耗尽。

mysqlbinlog 读取 binlog 文件报错“Failed to open file”
常见原因是权限或路径问题,mysqlbinlog 默认不走 MySQL 用户权限体系,而是直接读取文件系统。必须确保运行命令的系统用户(比如 ubuntu 或 mysql)对 binlog 文件有读权限,且 SELinux/AppArmor 没拦截。
- 确认 binlog 路径:查
SHOW VARIABLES LIKE 'log_bin_basename';,得到类似/var/lib/mysql/mysql-bin,实际文件是/var/lib/mysql/mysql-bin.000001这种 - 别用相对路径:
mysqlbinlog mysql-bin.000001很可能失败,必须写绝对路径,例如mysqlbinlog /var/lib/mysql/mysql-bin.000001 - 如果文件在远程机器上,不能直接 ssh 后执行——
mysqlbinlog不支持 ssh 协议,得先scp下来再解析
输出全是十六进制和乱码,怎么变人类可读
默认输出的是原始事件格式,含二进制字段和 base64 编码的语句。要看到 SQL,必须加 --base64-output=DECODE-ROWS 和 -v(或 --verbose),否则 INSERT、UPDATE 都显示为 # at 1234 + 一堆 ### 行。
- 基础可读命令:
mysqlbinlog --base64-output=DECODE-ROWS -v /var/lib/mysql/mysql-bin.000001 - 想过滤时间范围:加
--start-datetime="2024-05-20 10:00:00" --stop-datetime="2024-05-20 11:00:00" - 只看某个库:加
--database=myapp(注意:这个参数对 ROW 格式日志效果有限,它只过滤 Use_db 事件,不真正按表过滤)
为什么 --base64-output=DECODE-ROWS 有时还是看不到 SQL
根本原因在于 binlog_format 设置。如果服务器是 ROW 格式(MySQL 5.7+ 默认),mysqlbinlog 就不会还原成原始 SQL,而是展示行变更细节(### UPDATE `t` SET @1=2 WHERE @1=1)。这不是 bug,是设计使然。
-
STATEMENT格式下,-v能直接看到原始INSERT INTO ...;但该模式在函数、时间相关语句下不可靠,生产环境极少用 -
MIXED格式混合使用,但具体哪条走哪条由 MySQL 决定,mysqlbinlog不会帮你“猜”出原始语句 - 真要还原成 SQL,得用第三方工具如
binlog2sql,它基于mysqlbinlog -v输出做二次解析,但要注意它不处理 DDL 和 GTID 边界
解析大 binlog 文件卡住或内存爆掉
mysqlbinlog 是单线程、全加载式解析,遇到几百 MB 的 binlog,尤其带大量 BLOB/TEXT 字段时,会吃光内存或拖慢到无法接受。
- 优先用时间或位置截断:
--start-position=123456 --stop-position=123500比全量解析快十倍 - 避免
-v -v(双重 verbose):第二层 -v 会尝试反解字符集,触发大量字符串操作,基本只在调试编码问题时才需要 - 不要重定向到终端看长输出:用
mysqlbinlog ... | less或先存文件mysqlbinlog ... > out.sql,否则终端渲染也会假死
最常被忽略的一点:mysqlbinlog 解析结果里的 SET @@SESSION.GTID_NEXT 和 ANONYMOUS_GTID_LOG_EVENT 不代表事务是否提交,它们只是复制位点标记。想确认某条更新是否生效,得结合 COMMIT 事件位置和下游从库同步状态来看,不能只扫一眼输出就下结论。











