MySQL执行Update字段值未变,存储引擎去重更新流是否记录日志?

2026-04-30 21:252阅读0评论SEO资源
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计804个文字,预计阅读时间需要4分钟。

MySQL执行Update字段值未变,存储引擎去重更新流是否记录日志?

不会写binlog(默认配置下)。在执行UPDATE时,MySQL会先读取原始值,再比较新旧值;如果所有被更新的字段值与原值完全一致(类型、NULL属性、字节级内容均相同),则Server层会跳过实际更新操作,自然也不会触发binlog写入。但这个行为会受到存储引擎和SQL模式的影响,不能一概而论。

InnoDB 对“无变化更新”的处理:不修改页、不刷脏、不记 undo log

InnoDB 在执行 UPDATE 前会调用 row_upd_changes_to_persistent() 判断是否真有变更。关键点如下:

  • 比较基于字段的内部存储格式(例如 TIMESTAMP 会转为秒级整数再比,VARCHAR 区分尾部空格与否取决于 collation
  • 即使 SQL 写了 SET name = name,只要该列值未变,InnoDB 不会标记记录为“已修改”,对应的数据页不会变脏,innodb_buffer_pool_pages_dirty 不增
  • 不生成新的 undo log 版本,MVCC 快照不受影响
  • 但会加 X 锁(或意向锁)并立即释放——锁开销仍在,只是没持久化动作

binlog 写入时机由 server 层控制,但受 storage engine 返回结果驱动

MySQL Server 层在调用 handler::update_row() 后,检查返回值:HA_ERR_RECORD_IS_THE_SAME 表示引擎明确告知“没变”,此时 Query_log_event 不生成。但要注意:

  • 若开启 binlog_row_image = MINIMAL,且某列未出现在 WHERE 或 SET 中,InnoDB 可能无法准确判断是否变更(尤其涉及函数或表达式时)
  • sql_modeSTRICT_TRANS_TABLESERROR_FOR_DIVISION_BY_ZERO 时,某些隐式转换可能让“看似相等”的值实际触发更新(如 '1' = 1 在非严格模式下成立,但更新 INT 字段赋字符串可能触发截断+重写)
  • 使用 INSERT ... ON DUPLICATE KEY UPDATE 时,即使最终 SET 值未变,只要发生冲突检测并进入 UPDATE 分支,仍会走完整更新流程(含 binlog)

实操验证方法:看 status、binlog 和 InnoDB 状态变量

快速确认某条 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_AFFECTEDNESTING_EVENT_TYPE
  • 注意:客户端显示 “0 rows affected” 是 server 层统计,不代表引擎层没做任何事(比如锁等待、解析、权限校验都已发生)

真正容易被忽略的是隐式类型转换和 collation 影响下的“逻辑相等 ≠ 存储相等”。比如 utf8mb4_0900_as_cs'a ''a' 不等,但 utf8mb4_general_ci 下可能被视作相同——这种差异会让同一句 SQL 在不同库上产生截然不同的日志行为。

标签:Mysql

本文共计804个文字,预计阅读时间需要4分钟。

MySQL执行Update字段值未变,存储引擎去重更新流是否记录日志?

不会写binlog(默认配置下)。在执行UPDATE时,MySQL会先读取原始值,再比较新旧值;如果所有被更新的字段值与原值完全一致(类型、NULL属性、字节级内容均相同),则Server层会跳过实际更新操作,自然也不会触发binlog写入。但这个行为会受到存储引擎和SQL模式的影响,不能一概而论。

InnoDB 对“无变化更新”的处理:不修改页、不刷脏、不记 undo log

InnoDB 在执行 UPDATE 前会调用 row_upd_changes_to_persistent() 判断是否真有变更。关键点如下:

  • 比较基于字段的内部存储格式(例如 TIMESTAMP 会转为秒级整数再比,VARCHAR 区分尾部空格与否取决于 collation
  • 即使 SQL 写了 SET name = name,只要该列值未变,InnoDB 不会标记记录为“已修改”,对应的数据页不会变脏,innodb_buffer_pool_pages_dirty 不增
  • 不生成新的 undo log 版本,MVCC 快照不受影响
  • 但会加 X 锁(或意向锁)并立即释放——锁开销仍在,只是没持久化动作

binlog 写入时机由 server 层控制,但受 storage engine 返回结果驱动

MySQL Server 层在调用 handler::update_row() 后,检查返回值:HA_ERR_RECORD_IS_THE_SAME 表示引擎明确告知“没变”,此时 Query_log_event 不生成。但要注意:

  • 若开启 binlog_row_image = MINIMAL,且某列未出现在 WHERE 或 SET 中,InnoDB 可能无法准确判断是否变更(尤其涉及函数或表达式时)
  • sql_modeSTRICT_TRANS_TABLESERROR_FOR_DIVISION_BY_ZERO 时,某些隐式转换可能让“看似相等”的值实际触发更新(如 '1' = 1 在非严格模式下成立,但更新 INT 字段赋字符串可能触发截断+重写)
  • 使用 INSERT ... ON DUPLICATE KEY UPDATE 时,即使最终 SET 值未变,只要发生冲突检测并进入 UPDATE 分支,仍会走完整更新流程(含 binlog)

实操验证方法:看 status、binlog 和 InnoDB 状态变量

快速确认某条 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_AFFECTEDNESTING_EVENT_TYPE
  • 注意:客户端显示 “0 rows affected” 是 server 层统计,不代表引擎层没做任何事(比如锁等待、解析、权限校验都已发生)

真正容易被忽略的是隐式类型转换和 collation 影响下的“逻辑相等 ≠ 存储相等”。比如 utf8mb4_0900_as_cs'a ''a' 不等,但 utf8mb4_general_ci 下可能被视作相同——这种差异会让同一句 SQL 在不同库上产生截然不同的日志行为。

标签:Mysql