【问题标题】:Postgres Dynamically Create SequencesPostgres 动态创建序列
【发布时间】:2018-03-17 12:51:09
【问题描述】:

我正在编写一个应用程序,其中有多个用户,用户可以在应用程序中上传报告。

目前,我有一个“报告”表,其中包含所有提交的报告,其中有一个“id”字段,该字段是表上的串行主键。

我指定的要求是,用户需要能够指定一个前缀和一个数字,以便他们的报告开始计数。例如,用户应该能够说他们的报告从 ABC-100 开始,然后下一个是 ABC-101、ABC-102,依此类推。

我正在考虑实现这一点的方式是,当用户创建帐户时,他可以指定前缀和起始编号,我将创建一个 postgres 序列,其中指定的前缀名称和 minValue 为用户希望报告开始的数字。

然后当用户提交新报告时,我可以将report_number 标记为nextval(prefix_sequence)。理论上这会起作用,但我对 postgres 还很陌生,我想要一些建议和反馈,看看这是否是对序列的良好使用,或者是否有更好的方法。

【问题讨论】:

  • 这些报告是否必须是连续的?因为序列不能保证......错误、重启和其他事情可能会导致序列中的间隙。

标签: database postgresql database-design


【解决方案1】:

这是一个您可能不需要序列的关键优势的领域——它们可以被多个事务同时使用。您可能也不想要相应的缺点,即序列中的间隙是正常的。如果您有并发事务、回滚等,得到类似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) ... 的子查询的幼稚方法。它完全是并发不安全的,如果同时运行多个事务,它会产生错误的结果或错误。而且速度很慢。

【讨论】:

  • 是的,这是一个比序列更好的解决方案。非常感谢!
  • 如何保证并发安全?这没有指定函数的 PARALLEL 状态。如果这个函数同时运行,会不会出现同样的问题?
  • Andrew:PARALLEL 目前不适用于写入,因为 Pg 仅并行读取。操作的读取部分必须在读取行之前对该行进行行锁,并且该锁一直保持到 tx 提交。这使得并发安全(并且可能非常慢)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-26
  • 2014-02-05
  • 2021-05-19
相关资源
最近更新 更多