如何从 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;
我有一个大麻烦
在我的函数中,我需要从 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;