LOGO 首页 OA教程 ERP教程 模切知识交流 PMS教程 CRM教程 技术文档 其他文档  
 
网站管理员

为什么UPDATE比SELECT危险得多?锁范围等于扫描行数

freeflydom
2026年10月13日 11:41 本文热度 206

先问一个问题。

一条UPDATE语句,WHERE条件精确匹配了10行,最终也只改了10行。你觉得它会锁多少行?

10行?

不对。锁的范围等于扫描的行数,不是最终修改的行数。

如果WHERE条件没走索引,InnoDB会全表扫描。扫过的每一行,都会被加上锁。扫描了100万行,就锁了100万行——即使最终只改了10行。

这就是所谓的“锁升级”。但严格来说,InnoDB根本没有“锁升级”这个机制。它不会主动把行锁升级为表锁。你看到的“等效表锁”,是WHERE条件没走索引导致的自然结果。

今天把这个反直觉的结论彻底拆开讲清楚。

先搞懂几个词:

记录锁(Record Lock) :锁住索引记录本身。
间隙锁(Gap Lock) :锁住索引记录之间的间隙,防止其他事务在间隙中插入数据。RR隔离级别下默认启用。
临键锁(Next-Key Lock) :记录锁+间隙锁的组合,InnoDB默认的锁模式。
扫描行数:InnoDB在执行UPDATE/DELETE时,通过索引或全表扫描定位目标行的过程中实际读取的行数。

一、为什么UPDATE/DELETE比SELECT危险得多

SELECT和UPDATE/DELETE在锁行为上有本质区别。

SELECT是“看一眼就走”——找到符合条件的行,读取数据,返回结果,不加锁(快照读)或加短暂的共享锁(当前读)。

UPDATE/DELETE是“找到了还要动手”——找到符合条件的行之后,需要修改数据,必须加排他锁。而且这个锁要一直持有到事务提交。

更关键的是:UPDATE/DELETE加锁的范围不是“最终修改的行”,而是“扫描过程中触及的所有行”。

二、锁升级的三种典型场景

场景一:WHERE条件列无索引

-- status列没有索引
UPDATE orders SET status = 'CANCELLED' WHERE status = 'PENDING';

如果status列没有索引,InnoDB只能全表扫描。扫描过程中,聚簇索引上的每一行都会被加上临键锁。如果表有500万行,等于锁了500万行。其他事务对这500万行中任何一行的修改都会被阻塞。

场景二:WHERE条件选择性极低

-- status='DONE'占全表的90%
UPDATE orders SET remark = 'processed' WHERE status = 'DONE';

即使status列有索引,如果status='DONE'占了全表的90%(450万行),优化器可能判断“走索引还不如全表扫描”。于是走全表扫描,锁住所有扫描过的行。

场景三:RR隔离级别下的间隙锁累积

RR隔离级别下,InnoDB不仅加记录锁,还会加间隙锁。全表扫描时,每两行之间的间隙都会被锁住。这意味着其他事务不仅不能修改已存在的行,也不能插入新行。

如果事务长时间不提交,这些间隙锁会一直持有,导致其他事务的INSERT操作被阻塞。

三、怎么确认锁的范围

方法一:查看当前锁等待

SELECT * FROM sys.innodb_lock_waits\G

如果有输出,说明当前有事务在等锁。看blocking_query字段,能直接定位是哪条SQL在阻塞别人。

方法二:查看InnoDB状态

SHOW ENGINE INNODB STATUS\G

在TRANSACTIONS段中,可以看到当前活跃事务持有的锁信息,包括锁的类型(记录锁、间隙锁、临键锁)和锁定的索引范围。

方法三:用EXPLAIN确认扫描行数

EXPLAIN UPDATE orders SET status = 'CANCELLED' WHERE status = 'PENDING';

关注type列和rows列。如果type=ALL(全表扫描)且rows很大,锁的范围就会很大。

四、解决方案

方案一:游标分批

不要一次性UPDATE/DELETE大量数据,而是分批执行。每批处理1000行,提交后再处理下一批。

-- 伪代码
WHILE (1=1) DO
    UPDATE orders SET status = 'CANCELLED' 
    WHERE status = 'PENDING' LIMIT 1000;
    IF ROW_COUNT() = 0 THEN LEAVE;
    COMMIT;
END WHILE;

方案二:索引优化

让WHERE条件走索引,减少扫描行数。这是最根本的解决方案。

-- 在status列上建索引
CREATE INDEX idx_status ON orders(status);

方案三:RC隔离级别+ROW格式binlog

如果业务允许,将隔离级别从RR改为RC。RC下没有间隙锁,锁的范围只限于匹配的行,不会锁住行之间的间隙。

同时确保binlog_format=ROW,这是RC下减少锁争用的前提条件。

方案四:用pt-archiver工具

Percona Toolkit的pt-archiver专门用于分批归档和删除数据。它会自动分批、自动提交、自动控制速度,避免大事务长时间持锁。

五、小结

InnoDB没有“锁升级”机制。“锁升级”是WHERE条件没走索引导致全表扫描、扫描过的每一行都被加锁的自然结果。锁的范围等于扫描行数,不是修改行数。避免锁升级的核心是让WHERE条件走索引,同时控制事务大小、缩短锁持有时间。RR隔离级别下间隙锁会加剧锁的范围,如果业务允许,考虑用RC隔离级别。


阅读原文:点击这里​


该文章在 2026/10/13 11:41:53 编辑过
关键字查询
相关文章
正在查询...
点晴ERP是一款针对中小制造业的专业生产管理软件系统,系统成熟度和易用性得到了国内大量中小企业的青睐。
点晴PMS码头管理系统主要针对港口码头集装箱与散货日常运作、调度、堆场、车队、财务费用、相关报表等业务管理,结合码头的业务特点,围绕调度、堆场作业而开发的。集技术的先进性、管理的有效性于一体,是物流码头及其他港口类企业的高效ERP管理信息系统。
点晴WMS仓储管理系统提供了货物产品管理,销售管理,采购管理,仓储管理,仓库管理,保质期管理,货位管理,库位管理,生产管理,WMS管理系统,标签打印,条形码,二维码管理,批号管理软件。
点晴免费OA是一款软件和通用服务都免费,不限功能、不限时间、不限用户的免费OA协同办公管理系统。
Copyright 2010-2026 ClickSun All Rights Reserved  粤ICP备13012886号-1  粤公网安备44030602007207号