在这个领域,您可能不需要序列的主要优势 - 它们可以由多个事务同时使用。您可能也不希望出现相应的缺点,即序列中的间隙是正常的。得到类似的输出是很正常的1, 2, 4, 7, 8, 12, ...
如果您有并发事务、回滚等。
在这种情况下,使用计数器会更好。当用户创建帐户时,在account_sequences
桌子像(account_id, counter)
. Do not将其存储在主账户表中,因为您将对其进行大量锁定和更新,并且您希望最小化VACUUM
工作量。
e.g.
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
。
这会起作用,因为每笔交易UPDATE
是相关的account_sequences
row 对其所持有的行进行锁定,直到提交或回滚。尝试获取同一账户 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)
$$;
顺便说一下,做not采用使用子查询的简单方法SELECT max(id) ...
。它完全是并发不安全的,如果同时运行多个事务,它会产生错误的结果或错误。而且速度很慢。