0

我有两个线程,这两个线程都试图阻止 20 个项目在 postgres 中形成一个序列。

当没有并发时,以下查询可以正常工作:

select last_value + 1 as start_seq,
  setval('sequence_name',
         (select last_value + 20 from sequence_name)) as end_seq
from sequence_name

If sequence's last value is 100, 
Thread T1 will get start_seq as 101 and end_seq as 120
Thread T2 will get start_seq as 121 and end_seq as 140
[considering T1 is started after T2]

现在假设两个线程试图同时阻塞 20 个项目。T1 和 T2 都可能将 start_seq 设置为 101,将 end_seq 设置为 120。

有没有一种方法可以原子地阻止序列?

我尝试使用 'RETURNING' 关键字使用 'update with select':

update sequence_name
  set last_value = last_value + 20
returning last_value - 20 + 1, last_value;

但它会引发错误 - '无法更改序列'sequence_name'',这实际上意味着您无法使用update 子句更新序列。

4

1 回答 1

0

没有必要保留任何东西。PostgreSQL 中的序列呈现非事务行为。每次调用nextval将不可逆地消耗至少一个 ID。

要从序列中获取接下来的 N 个数字,只需调用该nextval函数 N 次。像这儿:

#!/usr/bin/env python3
import psycopg2

def get_numbers_from_pg_sequence(cur, seq_name, limit):
    numbers = []
    while len(numbers) < limit:
        cur.execute("SELECT nextval(%s)", [seq_name])
        r = cur.fetchone()
        numbers.append(r[0])
    return numbers

with psycopg2.connect("dbname=test") as con:
    with con.cursor() as cur:
        cur.execute("CREATE SEQUENCE IF NOT EXISTS myseq")
        lst = get_numbers_from_pg_sequence(cur, "myseq", 5)
        print(lst)

如果您需要nextval非常频繁地运行(例如每秒数千次),那么您可以ALTER将序列的CACHE参数设置为大于默认值 1,并以“浪费”序列中的许多数字为代价进行一些小的优化,即使您在会话中只运行nextval一次。

请注意,不能保证连续分配 - 在某些情况下,您可能会获得不连续的唯一编号序列,即使在一次交易中也是如此。

必须阅读: CREATE SEQUENCE 文档底部的注释。

于 2019-12-28T16:43:00.740 回答