并发环境下如何按组选取首行且避免进程重复获取?
解决并发进程分组获取唯一首行的行锁问题
你的问题出在锁的时机太晚:CTE子句groups会先无锁查询全表并计算行号,多个并发进程此时会得到完全相同的rn=1的行集合,后续再对结果加锁时,已经有多个进程拿到了同一行的引用,导致重复获取。
正确的SQL查询(PostgreSQL环境)
利用PostgreSQL的DISTINCT ON特性直接获取分组首行,同时在查询阶段就加锁:
SELECT DISTINCT ON (a, b) * FROM tests ORDER BY a, b, id ASC FOR UPDATE SKIP LOCKED LIMIT 1;
原理说明
DISTINCT ON (a, b)会严格按照a、b分组,返回每组中id最小的首行(由ORDER BY a, b, id ASC保证)FOR UPDATE SKIP LOCKED会在查询过程中直接锁定选中的行,其他并发进程执行同一查询时,会自动跳过已被锁定的分组行,转而获取未被锁定的下一个分组首行LIMIT 1保证每个进程每次只获取一个分组的首行,符合业务需求
跨数据库兼容写法
如果你的数据库不支持DISTINCT ON,可改用提前锁定分组最小id的写法:
WITH locked_groups AS ( SELECT * FROM tests WHERE id IN ( SELECT MIN(id) FROM tests GROUP BY a, b ) FOR UPDATE SKIP LOCKED LIMIT 1 ) SELECT * FROM locked_groups;
关键注意事项
- 必须在事务上下文中执行该查询,处理完计算任务后及时提交事务,释放锁,让其他进程能获取剩余分组
内容的提问来源于stack exchange,提问作者str.gsub
相关产品推荐
相关产品推荐

