如何降低MySQL在RR隔离级别下的锁冲突与事务等待耗时

陌萱君_6489

陌萱君_6489

2026-11-01

489人浏览

原创

RR隔离级别下锁冲突高主因是间隙锁和临键锁不及时释放且范围失控;应将SELECT FOR UPDATE移至事务末尾、确保UPDATE走索引、用主键分批更新、空表INSERT用ON DUPLICATE KEY避免死锁。

如何降低mysql在rr隔离级别下的锁冲突与事务等待耗时

RR(REPEATABLE READ)隔离级别下锁冲突和事务等待耗时高,不是因为“锁太多”,而是因为间隙锁(Gap Lock)和临键锁(Next-Key Lock)在事务提交前不释放,且锁范围容易失控。不降级隔离级别也能显著缓解——关键在控制加锁时机、缩小锁粒度、堵住隐式锁扩大路径。

为什么SELECT FOR UPDATE要放在事务最后一步

很多人把SELECT FOR UPDATE写在事务开头,以为“先锁住再干活”更安全。实际它让锁提前 3~5 秒持有时长:中间若调用 RPC、解析 JSON、校验参数,锁就空等在那里,后面所有想改同一行的事务全卡住。

  • 正确做法:先做所有非 DB 操作(查缓存、验权限、算金额),只在真正要更新前一刻才执行SELECT FOR UPDATE或直接UPDATE ... WHERE ... AND status = 'pending'
  • 效果:锁持有时间从“整个业务逻辑耗时”压缩到“SQL 执行+网络往返”,通常压到 10ms 内
  • 注意:SELECT FOR UPDATE必须跟在BEGIN之后、COMMIT之前,否则会报错SAVEPOINT does not exist

UPDATE语句的WHERE条件必须命中索引,且不能有函数包裹

没索引的WHERE会让 InnoDB 全表扫描并给每行加记录锁;即使有索引,用了函数也会让优化器放弃走索引,结果一样是锁全表。

  • 危险写法:UPDATE orders SET status = 'done' WHERE DATE(created_at) = '2026-09-28' → created_at字段索引失效,触发全表扫描加锁
  • 安全写法:UPDATE orders SET status = 'done' WHERE created_at >= '2026-09-28 00:00:00' AND created_at
  • 检查方法:对语句执行EXPLAIN,确认type是range或ref,且key列显示实际使用的索引名

批量更新必须按主键分段 + 即时提交

一条UPDATE t SET x = 1 WHERE created_at > '2026-09-01'可能锁住几千行,且这些锁要等到整个事务结束才释放。并发稍高,后续请求全排队等这一个事务。

MySQL8 CRM Schema Design Expert
MySQL8 CRM Schema Design Expert

面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。

下载
  • 拆分方式:用主键范围切片,例如WHERE id BETWEEN 10000 AND 10999 AND created_at > '2026-09-01',每次最多更新 1000 行
  • 必须每批执行后立刻COMMIT,不能攒着一起提交——否则锁还是集中释放
  • 补充技巧:如果表有自增主键且数据写入时间与 ID 高度相关,可直接用id > ?代替时间条件,避免因时间字段无索引或索引选择率差导致锁扩大

空表或低数据量表插入死锁,本质是间隙锁争抢

RR 级别下,空表的主键间隙是(−∞, +∞),所有并发INSERT都在抢同一个 Insert Intention Lock,InnoDB 检测到循环等待后随机回滚一个事务,报错Deadlock found when trying to get lock。

  • 首选解法:INSERT INTO t (a, b) VALUES (?, ?) ON DUPLICATE KEY UPDATE b = VALUES(b),前提是a或b上有唯一索引(如业务单号)
  • 次选解法:会话级降为READ COMMITTED,执行SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED,此级别下间隙锁禁用,INSERT 只锁实际插入行
  • 慎用解法:REPLACE INTO或INSERT IGNORE,它们会先删后插或静默跳过,可能引发主键重用或 binlog 日志膨胀

最易被忽略的一点:RR 级别下,**锁永远落在索引上,而不是数据行上**。没有索引、索引未命中、用了非唯一二级索引,都会让锁范围指数级扩大——你以为只锁了一行,实际锁了整段间隙甚至全表。每次加锁前,先看EXPLAIN,再看SHOW ENGINE INNODB STATUS\G里的locks段,比调优参数实在得多。

相关专题

更多
数据分析工具有哪些
数据分析工具有哪些

数据分析工具有Excel、SQL、Python、R、Tableau、Power BI、SAS、SPSS和MATLAB等。详细介绍:1、Excel,具有强大的计算和数据处理功能;2、SQL,可以进行数据查询、过滤、排序、聚合等操作;3、Python,拥有丰富的数据分析库;4、R,拥有丰富的统计分析库和图形库;5、Tableau,提供了直观易用的用户界面等等。

2023.10.12

4663

8

SQL中distinct的用法
SQL中distinct的用法

SQL中distinct的语法是“SELECT DISTINCT column1, column2,...,FROM table_name;”。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2023.10.27

971

4

SQL中months_between使用方法
SQL中months_between使用方法

在SQL中,MONTHS_BETWEEN 是一个常见的函数,用于计算两个日期之间的月份差。想了解更多SQL的相关内容,可以阅读本专题下面的文章。

2024.02.23

1229

5

SQL出现5120错误解决方法
SQL出现5120错误解决方法

SQL Server错误5120是由于没有足够的权限来访问或操作指定的数据库或文件引起的。想了解更多sql错误的相关内容,可以阅读本专题下面的文章。

2024.03.06

6661

10

sql procedure语法错误解决方法
sql procedure语法错误解决方法

sql procedure语法错误解决办法:1、仔细检查错误消息;2、检查语法规则;3、检查括号和引号;4、检查变量和参数;5、检查关键字和函数;6、逐步调试;7、参考文档和示例。想了解更多语法错误的相关内容,可以阅读本专题下面的文章。

2024.03.06

3403

4

oracle数据库运行sql方法
oracle数据库运行sql方法

运行sql步骤包括:打开sql plus工具并连接到数据库。在提示符下输入sql语句。按enter键运行该语句。查看结果,错误消息或退出sql plus。想了解更多oracle数据库的相关内容,可以阅读本专题下面的文章。

2024.04.07

6640

11

sql中where的含义
sql中where的含义

sql中where子句用于从表中过滤数据,它基于指定条件选择特定的行。想了解更多where的相关内容,可以阅读本专题下面的文章。

2024.04.29

9161

6

sql中删除表的语句是什么
sql中删除表的语句是什么

sql中用于删除表的语句是drop table。语法为drop table table_name;该语句将永久删除指定表的表和数据。想了解更多sql的相关内容,可以阅读本专题下面的文章。

2024.04.29

1230

5

sql中删除一列的命令是什么
sql中删除一列的命令是什么

在sql中,使用alter table语句可以删除一列,语法为:alter table table_name drop column column_name。想了解更多sql的相关内容,可以阅读本专题下面的文章。

2024.04.29

1092

5

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
PostgreSQL vs MySQL
PostgreSQL vs MySQL

共1课时 | 193人学习