面试官问我:select......forupdate会锁表还是锁行?"

admin 2026-10-21 21:13:30 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

cover_image

面试官问我:select......for update会锁表还是锁行?

原创

库库
库库

数据库开发

2026年7月2日 16:59
福建

在小说阅读器读本章

去阅读

很多开发者第一次接触 SELECT ... FOR UPDATE,都是在解决并发更新问题时。

这个语句看起来很简单,却经常引出一个重要的问题:

SELECT ... FOR UPDATE 会锁整个表,还是只锁查询到的行?

简短的答案是:通常锁的是行,而不是整个表。 但完整的答案取决于数据库引擎、索引、事务隔离级别,甚至取决于 SQL 语句本身。

什么是 SELECT … FOR UPDATE?

SELECT ... FOR UPDATE 是一种加锁读取(locking read)。与普通的 SELECT 只负责读取数据不同,它告诉数据库,这些查询出来的行很可能会在当前事务中稍后被更新。

当这些行被锁定后:

  • 其他事务不能修改这些行。
  • 其他针对相同行执行的 SELECT … FOR UPDATE 必须等待。
  • 这些锁会一直保持到事务提交(commit)或回滚(rollback)。

正如 PostgreSQL 官方文档所说,\"FOR UPDATE会使SELECT语句返回的行按照更新操作的方式被锁定。\"

一个常见的误解:\"它会锁整个表\"

很多开发者认为,SELECT ... FOR UPDATE 一定会锁整张表。

实际上,现代关系型数据库都会尽可能使用行级锁(row-level locking)。

例如:

BEGIN;

SELECT *
FROM orders
WHEREid = 100
FORUPDATE;

UPDATE orders
SETstatus = 'PAID'
WHEREid = 100;

COMMIT;

这里只有 id = 100 这一行会被锁住。

其他事务仍然可以:

  • 读取其他行;
  • 更新其他行;
  • 插入无关的数据。

这种细粒度的加锁方式,也是 MySQL InnoDB 和 PostgreSQL 能够在高并发场景下保持良好扩展性的原因之一。

那为什么有人感觉\"锁了整张表\"?

如果 SELECT ... FOR UPDATE 正常情况下只锁行,那么为什么有些应用看起来像是把整个表都锁住了?

通常有下面几种情况。

1、没有合适的索引

假设执行下面这条 SQL:

SELECT *
FROM orders
WHERE customer_name = 'Alice'
FOR UPDATE;

如果 customer_name 没有索引,数据库就需要扫描整张表才能找到符合条件的数据。

虽然它的目标仍然是行级锁,但在执行过程中,可能会锁住表中大量的行,甚至锁住扫描到的所有行,从而大幅降低并发能力。在高并发场景下,这种阻塞效果往往与锁表十分接近。

数据库并不是主动进行了表锁,而是因为无法快速定位目标数据,只能锁住更多的行。

2、范围查询和 Gap Lock(MySQL InnoDB)

MySQL 的 InnoDB 存储引擎还引入了另外一个概念:Next-Key Lock(行锁 + Gap Lock)。

例如:

SELECT *
FROM orders
WHERE id BETWEEN 100 AND 200
FOR UPDATE;

除了锁住已经存在的数据行之外,InnoDB 还可能锁住这些索引记录之间的间隙(gap)。

这样做是为了防止其他事务向这个范围内插入新的数据,从而避免在默认的 REPEATABLE READ 隔离级别下出现幻读(phantom read)。

因此,开发者有时会误以为整个表都被锁住了,而实际上,被保护的是某一个索引范围。


3、没有匹配到任何数据

还有一种比较有意思的特殊情况,就是查询没有找到任何记录。

例如:

SELECT *
FROM orders
WHERE id = 9999
FOR UPDATE;

如果查询没有匹配到任何数据,情况则取决于事务隔离级别。

在 MySQL InnoDB 的默认 REPEATABLE READ(RR) 隔离级别下,即使没有找到 id = 9999 这条记录,数据库仍然可能获取 Gap Lock(间隙锁),锁住对应索引中的间隙,阻止其他事务插入 id = 9999 的记录,从而避免幻读(phantom read)。

而在 READ COMMITTED(RC) 隔离级别下,对于这种未命中记录的等值查询,一般不会获取 Gap Lock,因此不会因为查不到数据而阻塞后续插入。

4、不同的存储引擎

存储引擎也会影响锁的行为。

  • InnoDB

支持行级锁。
* MyISAM

使用表级锁。

因此,同样一条 SQL,在不同的存储引擎下,表现可能完全不同。

MariaDB 官方文档也提到,像 MyISAM 这样的非事务型存储引擎使用的是表级锁。

PostgreSQL 与 MySQL 的区别

虽然两者都支持 SELECT ... FOR UPDATE,但实现方式并不完全一样。

PostgreSQL

PostgreSQL 只会锁住查询返回的数据行。

它内部也会获取一个轻量级的表锁,用于协调元数据,但这个锁不会阻止其他事务正常读取或修改其他行。真正产生阻塞作用的是行级锁。

此外,PostgreSQL 的 FOR UPDATE 不会产生 Gap Lock(间隙锁),因此不存在 MySQL InnoDB 中因 Gap Lock 而阻止其他事务插入数据的情况。

MySQL InnoDB

InnoDB 同样优先使用行级锁。

但是,根据下面几个因素:

  • 事务隔离级别;
  • 是否使用索引;
  • 查询条件;

它还可能产生 Gap Lock 或 Next-Key Lock,因此实际锁定的范围可能比开发者预期的更大。

最佳实践

为了让 SELECT ... FOR UPDATE 获得最好的性能,可以遵循下面几点:

  • WHERE 条件中的字段尽量建立索引。
  • 事务尽量保持简短,让锁尽快释放。
  • 只锁真正需要更新的数据。
  • 避免在事务中执行全表扫描。
  • 涉及多行数据时,按照固定顺序获取锁,以降低死锁发生的概率。

PostgreSQL 社区中也经常建议采用一致的加锁顺序。


总结

SELECT ... FOR UPDATE 本质上是一种行锁机制(row-locking mechanism),而不是表锁命令。

不过,实际的加锁行为会受到多个因素影响:

  • 是否存在索引;
  • 数据库引擎;
  • 事务隔离级别;
  • 查询条件;
  • 存储引擎的加锁策略。

在 PostgreSQL 中,SELECT ... FOR UPDATE 主要锁定查询返回的那些行;而在 MySQL InnoDB 中,同样采用行级锁,但在某些情况下,Gap Lock 和 Next-Key Lock 会扩大实际的加锁范围。

理解这些细节,有助于解释为什么一条在开发环境看起来毫无问题的 SQL,在生产环境的高并发场景下,却可能表现出完全不同的行为。

与其问:

\"SELECT ... FOR UPDATE 到底锁表还是锁行?\"

经验丰富的数据库工程师更倾向于问:

\"这条 SQL 到底会锁住哪些行,以及哪些索引范围?\"

这两者之间的区别,往往正是构建高并发数据库应用的关键。


当Skills安全检测出来的时候,我们是否应该考虑---Skills木马化" 网络安全文章

当Skills安全检测出来的时候,我们是否应该考虑---Skills木马化"

本文深入分析了AISkills生态的安全风险,指出Skills本质是长文本提示工程,但存在严重供应链攻击威胁。文章以ClawHavoc事件为例,展示了恶意Skill通过域名仿冒、虚假星标等手段窃取数据,并揭示了权限滥用、免杀技术(如多态变形、零日利用)及AI-Agent自动化感染等高级攻击手法。作者强调用户安全意识薄弱,并提供了三看三查防御建议,呼吁加强生态治理。
脸是最诚实的情报源,如何从公开视频里读出谈判底牌" 网络安全文章

脸是最诚实的情报源,如何从公开视频里读出谈判底牌"

本文探讨了如何通过分析公开视频中的微表情来解读人物的真实情绪与谈判底牌。文章从心理学研究入手,解释了微表情是人类试图压抑真实情绪时在脸上一闪而过的反应,并介绍了系统化的分析方法FACS。随后,文章通过泽连斯基与特朗普的两次会面、阿拉斯加会谈等四个真实案例,详细展示了如何通过面部肌肉的细微变化(如眉头收拢、鼻翼收缩、单侧微笑)来判断人物的情绪状态、内心活动及沟通策略。最后,文章强调了进行微表情分析时应关注情绪变化的完整过程、言语与非言语的一致性以及建立基准状态等原则,指出这种分析能为战略决策提供有价值的开源
美国国家安全局正把量子计算变成了一条工业生产线" 网络安全文章

美国国家安全局正把量子计算变成了一条工业生产线"

美国正将量子计算作为战略工业管理,通过QuantumEAGLe计划绑定商业生态,同步推进量子计算机研发和后量子密码迁移,以应对先存后解威胁。核心挑战在于稀释制冷机、氦-3等关键组件的全球供应链脆弱性,需通过建厂和产业政策解决。2028-2029年被视为容错量子计算机出现的关键节点,攻防同步的时间窗口已明确。
评论:0   参与:  0