根本原因是MySQL日志生成依赖存储引擎返回的精确变更信号(如HA_ERR_RECORD_IS_THE_SAME),而非SQL语义判断;STATEMENT模式必写binlog,MINIMAL模式因字段读取不全易误判变更,ON DUPLICATE KEY UPDATE始终触发完整更新逻辑。

MySQL 执行 UPDATE 时赋值相同数据,仍可能产生 binlog(甚至 redo log / undo log),根本原因不是“写错了”,而是 MySQL 的日志生成逻辑不以“SQL 看似没变”为判断依据,而是依赖存储引擎返回的精确变更信号,且受多种配置干扰。
binlog_format = STATEMENT 时必然写 binlog
在 STATEMENT 模式下,MySQL server 层根本不读取原行数据,也不比对新旧值。它只把原始 SQL 字符串原样记入 binlog:
- 执行
UPDATE t SET name = 'alice' WHERE id = 1,哪怕name当前就是'alice',也会生成Query_log_event - 主从复制时,这条语句会在从库重放,可能触发锁、触发器、隐式类型转换(如
SET c = '1'更新 INT 列) -
sql_mode含STRICT_TRANS_TABLES时更危险:字符串'1'赋给 INT 列可能被截断为1,但若原值是0,则实际发生了变更
binlog_row_image = MINIMAL 时容易漏判“无变更”
ROW 模式下,是否写 binlog 取决于 InnoDB 返回的错误码:HA_ERR_RECORD_IS_THE_SAME 才跳过。而 MINIMAL 模式会让 InnoDB 无法读全字段,导致误判:
- 表结构为
t(id PK, a INT, b TEXT),执行UPDATE t SET a = 5 WHERE id = 1→b不在 SET 子句中,InnoDB 不读取其当前值 - 无法确认整行是否真未变,于是走完整更新路径(加锁、标记脏页),返回非
HA_ERR_RECORD_IS_THE_SAME,server 层照常写 binlog -
FULL模式可规避:强制读取所有列,确保比对完整
ON DUPLICATE KEY UPDATE 是特例,永远写 binlog
只要发生唯一键/主键冲突并进入 UPDATE 分支,无论 SET 值是否与原值一致,MySQL 都会执行完整更新流程:
安装、修复、更新或打包一个基于 Python 和 PyMySQL 实现的 Windows `mysql` 命令。适用于用户希望在 PowerShell/CMD 中输入 `mysql` 时重定向到 Python 实现、需要交互式输入 MySQL 连接信息、需要同步到当前用户本地 bin 目录,或需要将该能力封装成可公开复用的 Codex skill。
- InnoDB 仍调用
update_row(),server 层收到的是“已执行”的结果,而非HA_ERR_RECORD_IS_THE_SAME - binlog 中必然出现
Update_rows_event(ROW 模式)或对应 SQL(STATEMENT 模式) - 不能套用普通
UPDATE的“去重”经验
如何验证某次 UPDATE 是否真实变更?
别只看 0 rows affected —— 它不可靠。应交叉检查三项:
- 查
SHOW STATUS LIKE 'Innodb_buffer_pool_pages_dirty':不变说明没刷脏页 - 用
mysqlbinlog --base64-output=DECODE-ROWS -v检查事务里是否有Table_map+Update_rows事件 - 开
performance_schema,查events_statements_history_long表中的ROWS_AFFECTED和NESTING_EVENT_TYPE
真正影响日志写入的,是 InnoDB 底层字段级的字节比对结果,以及 binlog 格式、镜像模式、SQL 模式三者的组合效果。任何一环松动,看似“无意义”的 UPDATE 就可能变成日志洪流的源头。










