为什么UPDATE比SELECT危险得多?锁范围等于扫描行数
当前位置:点晴教程→知识管理交流
→『 技术文档交流 』
先问一个问题。 一条UPDATE语句,WHERE条件精确匹配了10行,最终也只改了10行。你觉得它会锁多少行? 10行? 不对。锁的范围等于扫描的行数,不是最终修改的行数。 如果WHERE条件没走索引,InnoDB会全表扫描。扫过的每一行,都会被加上锁。扫描了100万行,就锁了100万行——即使最终只改了10行。 这就是所谓的“锁升级”。但严格来说,InnoDB根本没有“锁升级”这个机制。它不会主动把行锁升级为表锁。你看到的“等效表锁”,是WHERE条件没走索引导致的自然结果。 今天把这个反直觉的结论彻底拆开讲清楚。 先搞懂几个词: 记录锁(Record Lock) :锁住索引记录本身。 一、为什么UPDATE/DELETE比SELECT危险得多SELECT和UPDATE/DELETE在锁行为上有本质区别。 SELECT是“看一眼就走”——找到符合条件的行,读取数据,返回结果,不加锁(快照读)或加短暂的共享锁(当前读)。 UPDATE/DELETE是“找到了还要动手”——找到符合条件的行之后,需要修改数据,必须加排他锁。而且这个锁要一直持有到事务提交。 更关键的是:UPDATE/DELETE加锁的范围不是“最终修改的行”,而是“扫描过程中触及的所有行”。 二、锁升级的三种典型场景场景一:WHERE条件列无索引
如果 场景二:WHERE条件选择性极低
即使 场景三:RR隔离级别下的间隙锁累积 RR隔离级别下,InnoDB不仅加记录锁,还会加间隙锁。全表扫描时,每两行之间的间隙都会被锁住。这意味着其他事务不仅不能修改已存在的行,也不能插入新行。 如果事务长时间不提交,这些间隙锁会一直持有,导致其他事务的INSERT操作被阻塞。 三、怎么确认锁的范围方法一:查看当前锁等待
如果有输出,说明当前有事务在等锁。看 方法二:查看InnoDB状态
在 方法三:用EXPLAIN确认扫描行数
关注 四、解决方案方案一:游标分批 不要一次性UPDATE/DELETE大量数据,而是分批执行。每批处理1000行,提交后再处理下一批。
方案二:索引优化 让WHERE条件走索引,减少扫描行数。这是最根本的解决方案。
方案三:RC隔离级别+ROW格式binlog 如果业务允许,将隔离级别从RR改为RC。RC下没有间隙锁,锁的范围只限于匹配的行,不会锁住行之间的间隙。 同时确保 方案四:用pt-archiver工具 Percona Toolkit的 五、小结InnoDB没有“锁升级”机制。“锁升级”是WHERE条件没走索引导致全表扫描、扫描过的每一行都被加锁的自然结果。锁的范围等于扫描行数,不是修改行数。避免锁升级的核心是让WHERE条件走索引,同时控制事务大小、缩短锁持有时间。RR隔离级别下间隙锁会加剧锁的范围,如果业务允许,考虑用RC隔离级别。 阅读原文:点击这里 该文章在 2026/10/13 11:41:53 编辑过 |
关键字查询
相关文章
正在查询... |