mysql执行update相同值时并非跳过,而是完整走完加锁、读取、写undo/redo log、修改缓冲池、提交等流程,仅数据内容未变;因需保障并发一致性、触发器执行及mvcc可见性,必须加x锁,且仍消耗锁、日志、内存等资源。

UPDATE相同值时MySQL到底做了什么
MySQL执行UPDATE t SET a = 2 WHERE id = 1(而当前a已是2)时,**不是“跳过”操作,而是完整走完更新流程**:加行锁、读取当前值、写undo log、修改buffer pool中的数据页、生成redo log、刷盘、提交事务——只是最终没改变数据内容。所以返回0 rows affected,但资源消耗真实发生。
为什么必须加锁?不加锁会出问题
即使值未变,InnoDB仍会对匹配行加X锁(排他锁),原因很实在:
- 防止并发事务在你读取旧值后、判断是否更新前,抢先改掉该行(否则可能破坏一致性或触发器逻辑)
- 确保触发器(如
BEFORE UPDATE)能稳定获取“旧值”,哪怕新旧相同 - 维持MVCC可见性规则:其他事务的快照必须看到这个更新动作的“存在”,哪怕它没改数据
实测可验证:两个事务同时执行相同值的UPDATE,后执行者会被阻塞,直到前一个事务提交——说明锁确实被持有。
哪些资源被实际占用?
一次“无变更”的UPDATE仍会消耗:
-
innodb_row_lock_time计数器增加,锁等待时间计入统计 - redo log buffer中写入一条“无实质变化”的物理页修改记录(后续仍可能刷盘)
- undo log中保留一条版本链节点(用于回滚和MVCC),哪怕内容未变
- 每个连接独占的
sort_buffer_size、join_buffer_size等内存不释放(尤其当语句嵌套在复杂查询中) - 若涉及二级索引更新,即使主键值未变,索引维护开销仍存在
如何快速识别这类“伪空更新”
不是所有相同值更新都值得优化,但高频场景下值得关注:
- 应用层反复调用
UPDATE user SET status = ? WHERE id = ?,而status常未变 - ORM自动生成全字段UPDATE(如Hibernate
save()),字段值实际未修改 - 定时任务轮询更新状态字段,多数时候状态不变
查information_schema.innodb_trx配合performance_schema.data_locks,重点关注trx_rows_locked > 0但trx_rows_modified = 0的事务;再结合慢日志里Rows_examined远大于Rows_affected的UPDATE语句。
真正麻烦的不是单次开销,而是锁竞争+日志写入+缓冲区累积在高并发下放大成瓶颈。别只盯着0 rows affected就以为万事大吉。











