如何使PostgreSQL函数成为原子?

ins*_*ity 2 sql postgresql stored-procedures plpgsql psql

假设我有一些PostgreSQL函数,如下所示:

CREATE FUNCTION insertSth() RETURNS void AS $$
BEGIN
    INSERT INTO ...;
END;

CREATE FUNCTION removeSthAfterSelect() RETURNS TABLE(...) AS $$
BEGIN
     SELECT id INTO some_id ...;
     RETURN QUERY SELECT * FROM ...;
     DELETE FROM ... WHERE id = some_id;
END;

CREATE FUNCTION justDeleteSth() RETURNS void AS $$
BEGIN
     DELETE FROM ...;
END;

CREATE FUNCTION justSelectSth() RETURNS TABLE(...) AS $$
BEGIN
     RETURN SELECT * FROM ...;
END;
Run Code Online (Sandbox Code Playgroud)

从我对PostgresSQL函数的理解insertSth,justDeleteSth并将justSelectSth以原子方式执行(?).所以并行执行它们不会搞砸任何东西.

但是,removeSthAfterSelect如果有并行执行,它可能会SELECT id INTO some_id ..找到一些东西,然后同时另一个事务调用justDeleteSth并删除该行id = someId,所以当事务继续时它不会删除任何东西:DELETE FROM ... WHERE id = some_id;意味着它搞砸了.

是这样的吗?有没有办法避免这个问题?例如,说removeSthAfterSelect应该原子地执行?

Cra*_*ger 11

事务具有原子提交的属性,即保证整个事务生效,或者不执行任何事务.

这并不意味着交易无法互动.特别是,在READ COMMITTED模式中,通过另一个事务中途提交的事务可以具有可见效果.即使没有它,同时异常也是可能的和正常的.请参阅有关并发控制的PostgreSQL章节,尤其是事务隔离部分.与独立语句相比,函数语句对并发问题不再具有免疫力.

即使在单个语句中,也可能存在并发问题.陈述不是神奇的原子.人们通常认为,如果他们可以使用CTE,子查询等将所有内容打包到单个查询中,那么它就会神奇地免受并发问题的影响.事实并非如此.

没有函数标签可以说"以原子方式执行",因为您正在寻找的概念在DBMS中不存在.您将获得的最接近的是LOCK TABLE ... IN ACCESS EXCLUSIVE该函数使用的所有表,以便其他任何表都无法触及它们.如果您可以有效地推断并发和事务隔离,那通常是相当过度和不必要的.

很难更具体,因为你正在使用一个非常概括的例子,遗漏了所有的细节.例如,如果您尝试两次删除该行,为什么重要?

你应该研究的一些概念:

  • 快照
  • READ COMMITTEDSERIALIZABLE事务隔离
  • 行和表级锁,隐式(例如由DML采用的那些)和显式(例如SELECT ... FOR UPDATE)
  • 交易可见性
  • 在DML语句完成等待锁定后,谓词重新检查

作为并发操作的一个示例,请查看upsert问题.


但是对于removeSthAfterSelect,如果有并行执行,可能是SELECT id INTO some_id ..找到了什么,然后同时另一个事务调用justDeleteSth并删除id = someId的行,所以当事务继续时它不会删除任何东西: DELETE FROM ... WHERE id = some_id; 意思是它搞砸了.

你说的就好像一个交易停止而另一个交易运行,然后第一个交易继续.通常情况并非如此; 事物可以完全同时运行,许多陈述真正同时发生.

限制行的主要因素是行级锁定.在这种情况下,存在竞争条件,因为两者都DELETE试图获取行的行更新锁.无论哪个获取它将继续并删除该行.另一个DELETE被卡在行锁上,直到获胜的事务提交或回滚.如果它回滚,就好像什么都没发生一样,等待的交易继续正常进行.如果获胜事务提交删除,则等待事务看到锁已被释放,并且(在READ COMMITTED模式中)重新检查WHERE子句谓词以确保该行仍然匹配,发现它不再存在,并继续没有错误,因为删除零行不是错误.

在PL/PgSQL中,如果要强制语句只影响一行,并且RAISE EXCEPTION它与预期的受影响行不匹配,则可以检查受影响的行数.还有INTO STRICTSELECT.