mysql并发事务是如何调度的_mysql事务并发模型解析

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

MySQL事务调度本质是“锁 + MVCC + 隔离级别”的协同决策

MySQL本身没有独立叫“事务调度器”的可配置模块,它的并发调度逻辑内嵌在InnoDB存储引擎中,由

隔离级别
行锁机制
MVCC多版本控制
三者实时协同决定——不是先排队再执行,而是“边读边判、边写边锁、边查边快照”。比如你执行
SELECT ... FOR UPDATE
,InnoDB立刻加X锁并阻塞其他写;而普通
SELECT
REPEATABLE READ
下则直接走当前事务的
ReadView
找快照版本,不争锁也不等。

不同隔离级别对应完全不同的并发路径

别只记“RR默认”,要清楚每种级别背后触发的是哪套机制:

READ UNCOMMITTED
:跳过MVCC和锁检查,直接读最新行数据(可能脏读),几乎不调度,纯性能模式
READ COMMITTED
:每次
SELECT
都生成新
ReadView
,只读已提交版本;
UPDATE
仍需加X锁,但锁释放得早(语句结束即放)
REPEATABLE READ
(InnoDB默认):事务首次
SELECT
时建一次
ReadView
,后续全用它;
UPDATE
加X锁+间隙锁(防止幻读),锁持续到事务结束
SERIALIZABLE
:所有
SELECT
自动转为
SELECT ... LOCK IN SHARE MODE
,强制读锁,彻底串行化

实操建议:

set transaction isolation level read committed;
在高并发读写混合场景(如订单状态轮询+更新)比默认RR更少锁等待,但你要接受同一事务内两次
SELECT
结果可能不一致。

写冲突时,InnoDB靠“记录锁+间隙锁”抢夺执行权

当两个事务同时想改同一行,比如都执行

UPDATE users SET balance = balance - 10 WHERE id = 123;
,InnoDB不会让它们“协商谁先来”,而是:

事务A先到达:获取该行的
X锁
(排他锁),继续执行
事务B后到达:发现锁被占,进入
innodb_lock_wait_timeout
等待队列(默认50秒)
若超时未获锁,报错
ERROR 1205 (40001): Deadlock found when trying to get lock
Lock wait timeout exceeded

注意坑点:

WHERE
条件没走索引?那会升级成
表级锁
,整个表卡住;
UPDATE
带范围条件(如
WHERE created_at > '2025-01-01'
)还会触发
间隙锁
,把不存在的“空档”也锁住,导致插入被阻塞——这常被误认为“死锁”,其实是设计使然。

死锁不是异常,是InnoDB主动裁决的结果

死锁检测不是事后报错,而是每秒运行的后台线程在扫描锁依赖图。一旦发现环形等待(如事务A锁了行1等行2,事务B锁了行2等行1),InnoDB立刻选一个事务回滚(通常是undo log写得少的那个),让另一个继续。这不是bug,是保证系统活性的必要机制。

避免它关键不在“加锁顺序”,而在缩短锁持有时间:

UPDATE
DELETE
尽量靠近事务结尾,前面只做查询
避免在事务里调外部HTTP接口或长循环
SELECT ... FOR UPDATE
提前锁定,但必须确保WHERE条件精准命中索引

真正难调试的是“隐式锁升级”——比如

INSERT
在唯一索引冲突时会临时加S锁再转X锁,这种细节不看
SHOW ENGINE INNODB STATUS
里的
LATEST DETECTED DEADLOCK
段根本看不到。

相关推荐