假设我有一些 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;

根据我的理解,PostgresSQL 函数 insertSthjustDeleteSthjustSelectSth 将被原子地执行(?)。所以它们的并行执行不会搞砸任何事情。

但是对于 removeSthAfterSelect 如果存在并行执行,则可能是 SELECT id INTO some_id .. 找到了一些东西,然后同时另一个事务调用 justDeleteSth 并删除带有 id = someId 的行,所以当事务继续时,它不会删除这里的任何内容:DELETE FROM ... WHERE id = some_id; 意味着它把事情搞砸了。

是这种情况吗?
有没有办法避免这个问题?例如。通过说 removeSthAfterSelect 应该以原子方式执行?

最佳答案

事务具有原子提交的特性,即保证整个事务生效,或者都不生效。

这并不意味着事务不能交互。特别是,在 READ COMMITTED 模式下,一个事务在中途提交另一个事务可能会产生明显的影响。即使没有,同时出现异常也是可能的,也是正常的。参见 the PostgreSQL chapter on concurrency control ,特别是 transaction isolation 部分。
函数中的语句并不比独立语句更不受并发问题的影响。

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

没有函数标签说“以原子方式执行这个”,因为您正在寻找的概念在 DBMS 中不存在。您将获得的最接近的是 LOCK TABLE ... IN ACCESS EXCLUSIVE 函数使用的所有表,以便没有其他东西可以触及它们。如果您可以有效地推理并发性和事务隔离,那通常是多余且不必要的。

很难说得更具体,因为您使用的是一个非常笼统的示例,所有细节都被遗漏了。例如,为什么尝试删除该行两次很重要?

你应该学习的几个概念:

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

  • 作为并发运行的一个例子,看看 upsert problem



    你说的好像一个事务停止而另一个运行,然后第一个继续。情况往往并非如此。事物可以完全并发运行,许多语句真正同时发生。

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

    在 PL/PgSQL 中,如果要强制执行语句只影响一行,可以检查受影响的行数,如果它与预期的受影响行不匹配,则可以检查 RAISE EXCEPTIONINTO STRICT 也有 SELECT

    关于sql - 如何使 PostgreSQL 函数原子化?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/26076416/

    10-15 07:44