bigywater.tech
MySQL / InnoDB · 16

FOR UPDATE:锁定读和普通一致性读的区别

用一个连接FOR UPDATE锁住一行,另外两个连接分别用普通SELECT和UPDATE去碰它,看一致性读和锁定读的分界线在哪里。

0.0001s
普通一致性读完全不受锁影响
1.5029s
UPDATE被阻塞的时长,≈A的提交延迟
MVCC vs 锁
读操作与写操作走的是两条不同路径

做的事:A锁住一行,B和C分别用两种方式去碰它

A用SELECT ... FOR UPDATE锁住一行不提交,B用普通SELECT读它,C用UPDATE改它。

操作A: START TRANSACTION A: SELECT v FROM t4 WHERE id=1 FOR UPDATE → 锁住id=1这一行,v=100 B: SELECT v FROM t4 WHERE id=1 (普通一致性读,不加锁) C: UPDATE t4 SET v=999 WHERE id=1 (需要获取写锁) 1.5秒后 A: COMMIT

验证:B完全不受影响,C被卡住1.5秒

同样是访问A锁住的那一行,B和C的遭遇完全不同。

结果B的普通SELECT:耗时 0.0001 秒,立刻拿到 v=100 C的UPDATE:卡住,在A提交前一直等待 1.5秒后Acommit C的UPDATE完成,总耗时 1.5029 秒(几乎正好等于A的提交延迟)

B的普通SELECT完全没有被A的 FOR UPDATE 锁影响,0.0001秒瞬间返回——这就是"一致性读":普通SELECT走的是MVCC快照读,不需要获取任何锁,也就不会被别的事务持有的锁挡住。而C的UPDATE被阻塞了整整1.5秒,直到A提交才完成——因为UPDATE要获取这一行的排他锁,而A的 FOR UPDATE 已经持有了这把锁。

这就是"锁定读"和普通"一致性读"的分界线:只要不显式用 FOR UPDATE、LOCK IN SHARE MODE 这类语法,或者不是UPDATE/DELETE这种写操作,SELECT走的都是不加锁的快照读,跟其他事务的锁完全绝缘;一旦涉及修改数据(或显式要求锁定),才会去排队等锁。

分步演示:一致性读与锁定读的分界线

每一步都是本文上面用三个MySQL连接跑出来的原始结果。

同一行被锁住,普通读毫秒级通过,写操作排队等锁
这一步新验证的操作

    本文用到的工具

    • 本机 MySQL 26.7.0(InnoDB)
    • pymysql + threading:开三个独立连接模拟A、B、C三个并发会话
    • SELECT ... FOR UPDATE:获取排他锁的锁定读
    京ICP备2026067502号-1京公网安备11010502063487号