PostgreSQL序列的setval和nextval是否支持并发安全?

4

我想在PostgreSQL中按一定的batchSize来递增序列,但我不确定这个查询是否能够安全地处理对同一序列的并发调用。

select setval('my_sequence', nextval('my_sequence') + batchSize);

我对上述查询感到担忧的是,调用序列的setval()可能会将序列设置回去,或者可能会中止查询,因为如果另一个线程同时使用nextval()获取了它。

例如:

线程1:nextval = 1001 batchSize = 100 将序列设置为1101

线程2:nextval = 1002 batchSize = 20 将序列设置为1022

这意味着序列最终可能会返回在1002和1101之间的重复序列ID。

使用generate_series()函数也可以实现同样的效果。缺点是该序列不能保证按顺序排列,因为其他线程可能会同时调用同一序列的nextval(),这意味着我必须获取并解析生成的序列。

3个回答

3
PG文档中得知:
为了避免阻塞同时获取序列号的并发事务,nextval操作永远不会回滚;也就是说,一旦获取了一个值,它就被视为已使用,并且不会再次返回。即使周围的事务稍后失败,或者调用查询最终未使用该值,这也是正确的。例如,在检测到导致其遵循ON CONFLICT规则的任何冲突之前,带有ON CONFLICT子句的INSERT将计算要插入的元组,包括执行任何必需的nextval调用。这些情况将在分配值的序列中留下未使用的“空洞”。因此,PostgreSQL序列对象不能用于获取“无间隔”的序列。同样,如果事务回滚,则setval所做的任何序列状态更改都不会撤消。
我认为以上内容表明它是线程安全的。

2
基本上是在几乎相同的时间给出了完全相同的答案。现在才是一个并发问题:D - Alejandro
1
我同意!但是嘿...两个引用比一个更好! :) - Ftisiot
感谢您的回答,但我不确定是否解答了我的问题。我已经提供了更多关于这个问题的信息。 - hreinn

3
根据PostgreSQL文档,它是这样的:

这是原子性完成的:即使多个会话并发执行nextval,每个会话也将安全地接收一个不同的序列值。

这意味着,同时调用NEXTVAL是完全安全的,每个调用都会得到自己独特的值,并保证不重复。

谢谢您的回答,但我不确定这是否回答了我的问题。我已经提供了一些有关问题的更多信息。 - hreinn
2
@hreinn 鉴于此,我不确定序列甚至是正确的工具。它们从来没有保证返回连续的结果(回滚可能会导致间隙,而并发可能会导致交错)。序列适用于获取唯一值,但仅限于此。 - Alejandro
我可以使用generate_series()函数来实现上述功能,但我希望PostgreSQL的查询计划器能够确保在查询中对表进行setval和nextval函数的锁定。但正如你所指出的那样,我认为这可能并不是情况。 - hreinn
1
@hreinn 由于没有牵涉到表,也就不存在锁定的情况。并且正如 Ftisiot 的回答中所强调的那样,序列有意设计成不会阻塞。真正的问题是,你为什么需要它们被严格顺序执行?如果是这种情况,那么序列并不是正确的工具。 - Alejandro

1

根据PostgreSQL邮件列表中的答案,在并发场景下,SELECT setval(nextval() + N)不安全。一个安全且性能还算不错的替代方法是:

SELECT nextval('my_seq')
FROM generate_series(1, N)

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