已采纳
回答正文
回复
讨论正文
该问题的正文与回答。
背景:MySQL 8.0.36,InnoDB,REPEATABLE READ。一张 3200 万行的工单表 tickets,主键 id(自增),二级索引 idx_tenant_status (tenant_id, status, create_time)。业务有一个"领取工单"操作:
SELECT id FROM tickets WHERE tenant_id = ? AND status = 'PENDING' ORDER BY create_time ASC LIMIT 1 FOR UPDATE;
拿到的 id 之后再 UPDATE tickets SET status='PROCESSING', assignee=? WHERE id=?。
现象:
SHOW ENGINE INNODB STATUS 的 LATEST DETECTED DEADLOCK 里能看到 gap lock 等待,但两条语句锁的 id 并不重叠;SELECT COUNT(*) FROM tickets WHERE tenant_id=? 也被短暂阻塞(3–5s),理论上只读 RR 快照读不该被行锁挡住;performance_schema.data_locks 里能看到大量 LOCK_TYPE: GAP 记录,range 跨度从几千到十几万行不等,明显大于"LIMIT 1 应该锁 1 行"的直觉。已尝试:
ORDER BY create_time 去掉,让查询只锁"第一个命中的 PENDING 行",死锁少了但业务方不接受乱序领取;FOR UPDATE SKIP LOCKED,并发冲突消失了,但业务要求"必须领到最老的一单",SKIP LOCKED 会跳过被锁的行去领下一单,语义不符;想请教:
SELECT ... ORDER BY create_time LIMIT 1,再 UPDATE ... WHERE id=? AND status='PENDING' 用条件更新做 CAS,失败重试;(b) 在应用层用"按 tenant 分桶 + 桶内队列"把并发度降下来。想听听大家的实际取舍,哪种在生产上更稳?