用A锁住队列表的前2行不提交,对比B用SKIP LOCKED和C用普通FOR UPDATE的表现,看并发任务队列是怎么避免互相等待的。
典型的"多个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: 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并返回。
对照组:普通的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连接跑出来的原始结果。
FOR UPDATE SKIP LOCKED:验证跳过已锁定行的能力