这是一个您可能不需要序列的关键优势的领域——它们可以被多个事务同时使用。您可能也不想要相应的缺点,即序列中的间隙是正常的。如果您有并发事务、回滚等,得到类似1, 2, 4, 7, 8, 12, ... 的输出是很正常的。
在这种情况下,您最好使用柜台。当用户创建帐户时,在account_sequences 表中创建一行,例如(account_id, counter)。 不要将其存储在帐户的主表中,因为您将对其进行大量锁定和更新,并且您希望最大限度地减少 VACUUM 的工作量。
例如
CREATE TABLE account_sequences
(
account_id integer PRIMARY KEY REFERENCES account(id),
counter integer NOT NULL DEFAULT 1,
);
现在写一个简单的LANGUAGE SQL 函数
CREATE OR REPLACE FUNCTION account_get_next_id(integer)
RETURNS integer VOLATILE LANGUAGE sql AS
$$
UPDATE account_sequences
SET counter = counter + 1
WHERE account_id = $1
RETURNING counter
$$;
然后您可以使用它来代替nextval。
这将起作用,因为UPDATEs 相关account_sequences 行的每个事务都会对其持有的行进行锁定,直到它提交或回滚。尝试为同一帐户获取 ID 的其他事务将等待它完成。
更多信息请搜索“postgresql 无间隙序列”。
如果你愿意,你可以让你的 SQL 函数也获取前缀,使用 format 将它与生成的值连接起来,并返回一个 text 结果。如果您将 prefix text NOT NULL 列放入您的 account_sequences 表中,这会更容易,因此您可以执行以下操作:
CREATE OR REPLACE FUNCTION account_get_next_id(integer)
RETURNS text VOLATILE LANGUAGE sql AS
$$
UPDATE account_sequences
SET counter = counter + 1
WHERE account_id = $1
RETURNING format('%s%s', prefix, counter)
$$;
顺便说一句,不要采用带有SELECT max(id) ... 的子查询的幼稚方法。它完全是并发不安全的,如果同时运行多个事务,它会产生错误的结果或错误。而且速度很慢。