信息发布→ 登录 注册 退出

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

发布时间:2026-01-08

点击量:
MySQL事务调度本质是“锁 + MVCC + 隔离级别”的协同决策,由InnoDB内嵌实现,按隔离级别动态选择读快照、加锁策略与锁生命周期。

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

MySQL本身没有独立叫“事务调度器”的可配置模块,它的并发调度逻辑内嵌在InnoDB存储引擎中,由隔离级别行锁机制MVCC多版本控制三者实时协同决定——不是先排队再执行,而是“边读边判、边写边锁、边查边快照”。比如你执行SELECT ... FOR UPDATE,InnoDB立刻加X锁并阻塞其他写;而普通SELECTREPEATABLE 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 lockLock wait timeout exceeded

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

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

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

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

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

真正难调试的是“隐式锁升级”——比如INSERT在唯一索引冲突时会临时加S锁再转X锁,这种细节不看SHOW ENGINE INNODB STATUS里的LATEST DETECTED DEADLOCK段根本看不到。

标签:# http  # 两次  # 而在  # 还会  # 你要  # 首次  # 加锁  # 内嵌  # 报错  # 的是  # 死锁  # bug  # mysql  # 并发  # delete  # 线程  # 接口  # 循环  # Error  # select  # for  # 2025  # ai  
在线客服
服务热线

服务热线

4008888355

微信咨询
二维码
返回顶部
×二维码

截屏,微信识别二维码

打开微信

微信号已复制,请打开微信添加咨询详情!