MySQL 5.7+ 修改 binlog_format 需重启生效,仅 SET GLOBAL 不影响已有事务;必须主从统一设为 ROW 并写入配置文件,且确保表有主键或唯一非空索引,否则存在复制风险。

MySQL 5.7+ 直接改 binlog_format 可能不生效?
不是配置写错了,而是 MySQL 启动后若已开启 binlog,SET GLOBAL binlog_format = 'ROW' 仅对新会话生效,且主库已有事务仍按原格式记录。真正起效必须确保:主从两端都统一为 ROW,且修改在 binlog 初始化前完成(即服务重启时加载)。
- 临时设置(当前会话):
SET SESSION binlog_format = 'ROW'—— 只影响当前连接,重启或新连接失效 - 全局动态设置:
SET GLOBAL binlog_format = 'ROW'—— 影响后续新会话,但已有活跃事务、复制线程仍沿用旧格式 - 永久生效:必须写入配置文件(如
/etc/my.cnf或/etc/mysql/mysql.conf.d/mysqld.cnf)的[mysqld]段,并重启mysqld
配置文件里怎么写才安全?
别只加一行 binlog_format=ROW 就完事。ROW 格式依赖表有主键或唯一非空索引,否则从库执行 UPDATE/DELETE 时可能全表扫描甚至报错 1592 - Unsafe statement(虽然 5.7+ 默认禁用该警告,但隐患仍在)。
- 推荐写法(带注释和兼容性兜底):
binlog_format = ROW # 必须启用,否则 ROW 下无主键表的 DML 无法被正确复制 log_bin = /var/lib/mysql/mysql-bin # 推荐开启,便于定位 ROW 事件对应的原始 SQL(调试用) binlog_rows_query_log_events = ON
- 确认是否已启用 binlog:
SHOW VARIABLES LIKE 'log_bin';返回ON才有效;若为OFF,binlog_format设置会被忽略 - 5.7.7+ 版本支持在线切换,但需满足:
super_read_only=OFF且无活跃的 DDL 或 XA 事务
改完重启,怎么验证真的用了 ROW?
别只看变量值,要查实际生成的 binlog 内容。因为变量可能显示 ROW,但某些语句(如 INSERT ... SELECT 无确定性)仍被降级为 STATEMENT。
- 查当前会话格式:
SELECT @@session.binlog_format; - 查全局当前值:
SELECT @@global.binlog_format; - 抓一条 INSERT 后看 binlog 事件类型:
mysqlbinlog --base64-output=DECODE-ROWS -v /var/lib/mysql/mysql-bin.000001 | grep -A2 -B2 "Table_map\|Write_rows"
出现Table_map和Write_rows即为 ROW;若看到Query事件,则仍是 STATEMENT - 检查是否有隐式降级:
SHOW WARNINGS;在执行疑似不安全语句后立即运行,看是否提示Statement is not safe to log in statement format
主从不一致风险:从库也得同步改,且顺序不能错
如果只改主库,从库还是 MIXED 或 STATEMENT,它会尝试把 ROW 事件“翻译”回 SQL 执行,大概率失败或数据错乱。更糟的是,从库 binlog_format 不匹配还会导致 GTID 模式下复制中断。
- 操作顺序必须是:先停从库 SQL 线程 → 改从库配置并重启 → 再启 SQL 线程;不能边跑边切
- 从库也要设
binlog_format = ROW,即使它不产生 binlog(log_bin=OFF),因为复制解析逻辑仍依赖该变量 - 若用 GTID,改完后务必检查:
SELECT * FROM performance_schema.replication_applier_status_by_coordinator;确保LAST_ERROR_NUMBER为 0 - ROW 模式下,
binlog_row_image默认为FULL,但若做审计或闪回,可考虑设为MINIMAL或NOBLOB(注意兼容性)
改完别急着上线,先在测试环境用真实业务流量压测半天——ROW 格式会让 binlog 体积暴涨,尤其是大字段或批量更新场景,磁盘和网络压力会立刻体现出来。


















