bigywater.tech
MySQL / InnoDB · 25

SKIP LOCKED:并发任务如何跳过已锁定的行

用A锁住队列表的前2行不提交,对比B用SKIP LOCKED和C用普通FOR UPDATE的表现,看并发任务队列是怎么避免互相等待的。

0.0002s
SKIP LOCKED瞬间拿到未锁定行的耗时
1.0048s
普通FOR UPDATE被阻塞的耗时
并发不排队
SKIP LOCKED让worker各自拿不同的行

做的事:一张4行的任务队列表,A先锁住前2行

典型的"多个worker抢任务"场景。

代码CREATE TABLE queue1 (id INT PRIMARY KEY, status VARCHAR(20)); INSERT INTO queue1 VALUES (1,'pending'),(2,'pending'),(3,'pending'),(4,'pending'); A: START TRANSACTION A: SELECT id FROM queue1 WHERE status='pending' ORDER BY id LIMIT 2 FOR UPDATE → 锁住了 id=1, id=2(事务不提交)

验证:B用SKIP LOCKED,瞬间拿到剩下的行

同样的查询,B加了SKIP LOCKED。

结果B: SELECT id FROM queue1 WHERE status='pending' ORDER BY id LIMIT 2 FOR UPDATE SKIP LOCKED → 拿到: (3, 4) → 耗时: 0.0002 s

B完全没有等待——它的查询在扫描到id=1、id=2时,发现这两行已经被A锁住,SKIP LOCKED 让它直接跳过这两行,不等、不报错,继续往后找,立刻锁住了id=3、id=4并返回。

验证:C不加SKIP LOCKED,被阻塞到A提交为止

对照组:普通的FOR UPDATE。

结果C: SELECT id FROM queue1 WHERE status='pending' ORDER BY id LIMIT 2 FOR UPDATE 1秒后Acommit C拿到: (1, 2) C耗时: 1.0048 s(被阻塞到A释放锁为止)
对比很清楚:没有 SKIP LOCKED 的普通 FOR UPDATE,C老老实实排队等id=1、id=2被释放,拿到的还是A锁过的那两行;SKIP LOCKED 则让B完全不去凑这个热闹,直接去抢那些没人锁的行。这正是它为"任务队列"场景设计的能力——多个worker并发来抢任务时,用SKIP LOCKED能让它们各自拿到不同的任务并行处理,而不是排成一队互相等待,同一时刻只有一个worker在干活。

分步演示:任务队列是怎么被并发抢占的

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

SKIP LOCKED让并发worker各自拿到不同的行
这一步新验证的查询

    本文用到的工具

    • 本机 MySQL 26.7.0(InnoDB),4行任务队列表
    • pymysql + threading:模拟三个并发worker
    • FOR UPDATE SKIP LOCKED:验证跳过已锁定行的能力
    京ICP备2026067502号-1京公网安备11010502063487号