如何从 PostgreSQL 中的并发事务中的一个 table 的字段中获取最新的新 ID

How to get latest new id from one table's field from concurrent transaction in PostgreSQL

我有一个大麻烦

在我的函数中,我需要从 table 获取新 ID,然后使用新 ID +1 插入 table。

table 如下所示:

create table bill(
  flinkid bigint not null,
  fsystem text not null,
  fnaviid bigint not null,
  fbillid bigint not null,
  fdata jsonb,
  constraint bill_pkey primary key (flinkid, fsystem, fnaviid, fbillid)
);

在我的函数中,我需要从 table 中获取最大旧 fbillid,然后插入一个具有新 ID +1 的新行,如下所示:

do $$ declare
  vnewid bigint;
  vdata jsonb;
begin
  ...
  select coalesce(max(fbillid),0)+1 into vnewid from bill where flinkid=123 and fsystem='test' and fnaviid=123;
  ...
  insert into bill(flinkid,fsystem,fnaviid,fbillid,fdata)
  values(123,'test',123,vnewid,vdata);
  ...
  update bill set fdata=jsonb_set_lax(fdata,'{head,fbillno}',"XXX"::jsonb)
  where flinkid=123 and fsystem='test' and fnaviid=123 and fbillid=vnewid;
  ...
end;
$$ language plpgsql;

当遇到并发事务时,即两个或多个reqeust几乎同时调用函数,因为函数的事务需要一些时间来执行,第一个事务还没有提交,第二个事务访问table,字段fbillid是同一个。这将导致两行变成一行,并且行数据是第二行,第一行的数据丢失。

如何解决这个问题? 请多多指教,万分感谢!

您可以使用SELECT FOR UPDATE命令。 FOR UPDATE 导致 SELECT 语句检索的行被锁定,就好像要进行更新一样。这可以防止它们在当前事务结束之前被其他事务锁定、修改或删除。也就是说,其他尝试 UPDATE、DELETE、SELECT FOR UPDATE、SELECT FOR NO KEY UPDATE、SELECT FOR SHARE 或 SELECT FOR KEY SHARE 这些行的事务将被阻塞直到当前事务结束;相反,SELECT FOR UPDATE 将等待在同一行上具有 运行 任何这些命令的并发事务,然后将锁定并 return 更新的行。

例如:

  select fbillid into vnewid 
  from bill where fbillid = (select coalesce(max(fbillid),0) from bill)
  for update; 
  ------------ OR -------------- 
  select fbillid into vnewid 
  from bill 
  order by fbillid desc 
  limit 1
  for update; 

但是,完成您的操作后,您必须更新此锁定记录才能解锁:

  update bill 
  set 
    updated_date = now()
  where fbillid = vnewid;