mysql执行update语句经历了哪些步骤_mysql更新执行原理

来源:这里教程网 时间:2026-02-28 20:49:25 作者:

MySQL UPDATE 语句的执行流程从连接到落盘

MySQL 执行一条

UPDATE
不是简单地“改一行”,而是一套涉及连接、解析、优化、加锁、修改、写日志、刷盘的完整链路。实际执行中,哪一步卡住、慢或失败,直接决定你看到的是“秒回”还是“锁表半小时”。

典型路径是:

客户端连接 → 查询缓存(已弃用,8.0 移除)→ 解析 SQL → 权限校验 → 生成执行计划 → 存储引擎层定位记录 → 加锁(行锁/间隙锁/临键锁)→ 修改内存中的数据页 → 写入 redo log(prepare)→ 写入 binlog → redo log commit → 刷脏页(异步)

为什么 UPDATE 会锁表或锁住不相关的行

锁行为完全由存储引擎和隔离级别共同决定,InnoDB 下尤其容易踩坑。不是“有没有 WHERE 就锁全表”,而是“是否命中索引 + 隔离级别 + WHERE 条件能否使用索引下推”共同作用的结果。

没走索引的
WHERE
条件(如
WHERE name LIKE '%abc'
),InnoDB 可能退化为聚簇索引全扫描,对所有扫描到的记录加临键锁(Next-Key Lock)
REPEATABLE READ
下,即使只更新一行,也可能因间隙锁(Gap Lock)阻塞其他事务在相同间隙插入
唯一索引等值查询(
WHERE id = 100
)只加行锁;范围查询(
WHERE id > 100
)则加临键锁,覆盖记录+间隙
显式事务中未及时
COMMIT
,锁会一直持有,导致后续语句等待甚至
Lock wait timeout exceeded

redo log 和 binlog 怎么协同保证 crash safe

崩溃恢复靠的是两阶段提交(2PC):redo log 负责物理页变更的持久化,binlog 负责逻辑操作的归档与主从同步。两者必须一致,否则主从数据错乱或重启后数据丢失。

关键步骤顺序不可颠倒:

innodb_prepare → write binlog → innodb_commit
。如果 crash 发生在写完 binlog 但未 commit redo log 的中间状态,MySQL 启动时会回滚该事务;如果 redo log 已 commit 但 binlog 写失败,则依赖
binlog_checksum
和人工核对补救。

注意:

sync_binlog=1
innodb_flush_log_at_trx_commit=1
是强一致性前提,但会明显降低写性能。

UPDATE 很慢?先看执行计划和锁等待,别急着加索引

慢 UPDATE 常见原因不是 SQL 写得差,而是隐性资源争抢。先查

SHOW ENGINE INNODB STATUS\G
TRANSACTIONS
LATEST DETECTED DEADLOCK
,再结合
SELECT * FROM performance_schema.data_locks
(8.0+)确认锁对象。

执行计划显示
type: ALL
rows
极大 → 索引缺失或失效(如对字段做函数操作:
WHERE YEAR(create_time) = 2024
state: Updating
持续很久 → 可能被其他事务锁住,查
information_schema.INNODB_TRX
找 blocking_trx_id
大量
innodb_row_lock_waits
→ 锁冲突高,考虑拆分事务、调整隔离级别(如降为
READ COMMITTED
关闭间隙锁)、或业务层重试
频繁 update 同一热点行(如计数器)→ 考虑用
INSERT ... ON DUPLICATE KEY UPDATE
或应用层批量合并

真正影响 UPDATE 效率的,往往是锁等待时间和刷脏页压力,而不是 SQL 本身多复杂。

相关推荐