在Postgres 12数据库中,我在一个函数中有多个查询(SELECT
,UPDATE
,...),总共需要大约20分钟才能完成。如果status
没有运行,我在顶部有一个检查,它会执行UPDATE
:
create or replace function aaa.fnc_work() returns varchar as
$body$
begin
if (select count(*) from aaa.monitor where id='invoicing' and status='running')=0 then
return 'running';
else
update aaa.monitor set status='running' where id='invoicing';
end if;
--- rest of code ---
--finally
update aaa.monitor set status='idle' where id='invoicing';
return '';
exception when others then
return SQLERRM::varchar;
end
$body$
language plpgsql;
这个想法是为了防止其他用户执行--- rest of code ---
,直到status
空闲。
但是,其他人(调用相同的函数)似乎没有看到更新的状态,他们也继续执行--- rest of code ---
。如何在以下情况下强制提交:
update aaa.monitor set status='running' where id='invoicing'
;
以便所有其他用户会话可以看到更新的status
并相应地退出。
我需要交易吗?
2条答案
按热度按时间inb24sb21#
继续阅读我把最好的留到最后。
PROCEDURE
概念验证Postgres
FUNCTION
始终是原子的(在单个事务 Package 器中运行),不能处理事务。所以COMMIT
不允许。您可以使用dblink
的技巧来解决这个问题。参见:但是对于像这样的嵌套事务,可以考虑使用**
PROCEDURE
。* *Postgres 11您可以在这里管理交易:呼叫(重要!):
重要提示
在
UPDATE
之后添加COMMIT
。之后,并发事务可以看到更新的行。但是没有额外的
BEGIN
或START TRANSACTION
。The manual:在
CALL
命令调用的过程以及匿名代码块(DO
命令)中,可以使用COMMIT
和ROLLBACK
命令结束事务。在使用这些命令结束事务后,会自动启动新的事务,因此没有单独的START TRANSACTION
命令。(注意BEGIN
和END
在PL/pgSQL中有不同的含义。我们需要一个单独的PL/pgSQL code block,因为你有一个自定义的异常处理程序,并且(引用the manual):
事务不能在具有异常处理程序的块内结束。
(But我们可以在
EXCEPTION
处理程序中使用COMMIT
/ROLLBACK
。)不能在外部事务中调用此过程,也不能与任何其他DML语句一起调用此过程,否则将强制使用外部事务 Package 。必须是独立的
CALL
。参见:注意最后的
UPDATE aaa.monitor SET status = 'idle' WHERE ...
。否则(承诺!)status
将在异常后无限期地保持“运行”。关于从过程返回值:
我将
DEFAULT NULL
添加到INOUT
参数中,因此您不必在调用时提供参数。UPDATE
直接。如果行处于“running”状态,则不进行更新。(这也是逻辑的逻辑:你的IF
表达式似乎是向后的,因为当找到 no row withstatus='running'
时,它返回'running'。你似乎想要相反的东西)。我添加了一个(可选!)Assert以确保表
aaa.monitor
中的行存在。添加一个FOR KEY SHARE
锁,以消除Assert和后面的UPDATE
之间的竞争条件的微小时间窗口。锁定与删除或更新PK列冲突-但 * 不 * 与更新status
冲突。所以在正常操作中不会引发异常!手册:目前,
UPDATE
情况下考虑的列集是那些具有唯一索引的列,这些索引可以在外键中使用(因此不考虑部分索引和表达式索引),但将来可能会改变。SKIP LOCK
在锁冲突的情况下不等待。添加的异常永远不会发生。只是在演示防水概念验证。您的更新在
aaa.monitor
中显示了25行,因此我添加了参数_id
。上级法
上面的内容可能会让世界看到更多的信息。对于队列操作,有更有效的解决方案。使用锁代替,这对其他人来说是“可见的”。那么你不需要一个嵌套的事务开始,一个普通的
FUNCTION
就可以了:电话:
调用可以以任何您想要的方式嵌套。只要一个事务正在处理大任务,就不会启动其他事务。
同样,可选的assert取出一个
FOR KEY SHARE
锁,以消除竞争条件的时间窗口,并且添加的异常在正常操作中永远不会发生。我们根本不需要
status
列。行锁本身就是看门人。因此,PERFORM FROM aaa.monitor ...
中的SELECT
列表为空。附带利益:这也不会通过来回更新行而产生死元组。如果您出于其他原因仍然需要更新status
,那么您就回到了上一章的可见性问题。你可以把两者结合起来…关于
PERFORM
:关于行锁:
jgwigjjp2#
你试图完成的是一个自治事务。PostgreSQL没有一个简单的方法来做到这一点。此链接here讨论了一些替代方案。
但是,上面链接的文章中讨论的一种方法是使用PostgreSQL dblink扩展。
您需要将扩展添加到服务器
然后你就可以创建一个新的函数,这个函数可以在你的函数中调用
您可能需要考虑的另一件事是使用PostgreSQL锁。更多信息请参见here。