如何在PostgreSQL中以事务方式插入MAX(数字)+ 1的行而不使用序列

时间:2017-06-06 10:45:00

标签: postgresql

我有一个PostgreSQL表,可以简化为:

CREATE TABLE car(
  garage_id uuid,
  number integer
);

我需要在每个车库中,每辆车都有一个唯一的整数(Car N°1,Car N°2)。

序列的第一个想法

我的第一个想法是通过触发器为每个车库创建PostgreSQL序列,然后当我插入汽车时,我只需要为车库创建的序列调用nextval。 (如看过here

问题在于,对于每个车库,我需要创建一个序列。我的车库表很快会增长到1M行,根据这个帖子:Maximum number of sequences that can be created,过多的PostgreSQL序列确实会损害性能并使pgdump失败。

第二个想法

我发现了另一个想法on stackoverflow

第二个想法是创建另一个像

的表
CREATE TABLE garage_number(
  garage_id uuid,
  max_number integer
);

此表将保留最大车号,然后在每个插页中我们选择车库的max_number,并更新车库的max_number。

问题:目前它在并发方面不起作用(如果同时插入,则2辆车可以使用相同的号码)。

您是否知道我如何才能使其成为交易证明并且大规模工作?也许锁定garage_number表?

非常感谢你的帮助,

2 个答案:

答案 0 :(得分:2)

" 我如何才能使其成为交易证明并且大规模工作" - 你不能。最好的办法是尽量减少通过非序列方法生成数字时所需的锁定。

你的garage_number表可用于此,但我实际上不会在实际车库表上使用max(),而是这样:

create or replace function next_car_number(p_garage uuid)
  returns integer
as
$$
  update garage_number
     set max_number = max_number + 1
  where garage_id = p_garage
  returning max_number;
$$
language sql;

当然,您需要初始化garage_number一次,并且每次创建新车库时都需要在该表中插入一个新行(0的值为{{ 1}})

然后要分配一个新车号,您可以使用以下内容:

max_number

该车库的行将被锁定,直到该插入被提交,并且在此之前没有其他使用相同功能的插件可以完成。一旦提交,任何等待的事务都将看到新值。

在插入新车之前,您需要生成(并知道)车库的UUID - 但我猜是因为{引用了另一个名为insert into car (garage, number) values ('a0ee-bc99-9c0b-4ef8-bb6d-6bb9-bd38-0a11', next_car_number('a0ee-bc99-9c0b-4ef8-bb6d-6bb9-bd38-0a11')); 的表。 {1}}表,车库PK在插入新车时已经知道了。

这仍然序列化对"生成器的访问"对于单个车库,但会比为每个插入

运行garage查询更快

答案 1 :(得分:0)

如果你没有在一次交易中插入几辆汽车,也许值得尝试txid_current()?这个号码将与其他交易隔离开来,并且是唯一的。唯一明显但在这里 - 它不会是顺序的(你不能预测步骤,因为它取决于你执行你的交易量)。

像往常一样:

insert into car 
  (garage, number)
values 
  ('a0ee-bc99-9c0b-4ef8-bb6d-6bb9-bd38-0a11', txid_current());

当然,如果您在同一事务中多次运行上述语句,它将无效,因为txid_current()会相同。但是如果你将每个插入分开 - 这可能会起作用

https://www.postgresql.org/docs/current/static/functions-info.html