Postgres中使用limit进行选择以进行更新

5

我需要将处理记录的工作分配给不同的机器。

在每个机器(节点)上,我每分钟运行以下查询:

select * 
from RECORDS_TO_PROCESS
limit MAX_PER_MACHINE_RUN
for update

假设我有1000条记录需要处理,有10台机器(节点),每台机器每分钟最多要处理100条记录。
R1
R2
R3
...
R101
R102
R103
...
R201
R202
R203
...

我想要的是,查询Node1时只返回R1-100的记录,查询Node2时只返回R101-R200的记录等等。请问这样做是否可行?我们认为这样做能够分散负载吗?假设事务隔离级别是默认的Read Committed。
2个回答

6
那样做不行,因为第一个查询锁定一行以进行更新将会阻塞所有后续试图锁定该行的查询,直到第一个提交。版本9.5将引入“SKIP LOCKED”,使它们不会相互阻塞,而是获得不同的行。
在9.5之前,您可以在WHERE子句中使用pg_try_advisory_lock来模拟SKIP LOCKED功能。您需要安排每行具有唯一的标识符以提供给pg_try_advisory_lock。
但我认为这是一个错误的设计。你几乎总是想通过将某个标识符(如节点+pid)写入列并提交来首先声明一行,然后执行第二个事务以在完成后将其标记为已完成。
如果您将其限制为每分钟100次,我不认为有理由尝试一次选择100次。只需每隔半秒钟执行一次即可。除非您的数据库具有非常高的ping时间,否则开销应该是可以容忍的。

第一次查询锁定行以进行更新将阻止所有后续查询。很抱歉,我不理解这个。我们是说如果两个节点(N1和N2)分别查询获取100行(首先是N1,然后是N2),那么N2的查询将不得不等待返回其100条记录,直到N1完成处理其获取的100条记录吗? - user965692
如果第一笔交易在处理期间保持事务状态,那么是的,第二笔交易会被阻塞。如果第一笔交易在任何事务之外运行,则“FOR UPDATE”将无效。 - jjanes

1

SKIP LOCKED非常酷。我建议升级!

我对另一种方式(通过更新/提交先声明行)感到好奇。它真的比仅使用FOR UPDATE SKIP LOCKED更好吗?我看到的唯一优点是可移植性到其他RDBMS。


1
关于在处理项目时保留事务的危害的最新讨论,请参见 https://www.postgresql.org/message-id/flat/33bae6e3-1c5a-77f0-ee99-408fbf0fbb83%40gusw.net。直到最古老的未完成事务关闭,才能清理陈旧的行。每次想要一个新行的进程都必须深入查看锁定行和旧行的残留物,这很快会变得缓慢。 - jjanes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接