假设我有一些 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 函数
insertSth
、 justDeleteSth
和 justSelectSth
将被原子地执行(?)。所以它们的并行执行不会搞砸任何事情。但是对于
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
事务隔离 SELECT ... FOR UPDATE
) 作为并发运行的一个例子,看看 upsert problem 。
你说的好像一个事务停止而另一个运行,然后第一个继续。情况往往并非如此。事物可以完全并发运行,许多语句真正同时发生。
限制的主要内容是行级锁定。在这种情况下,存在竞争条件,因为两个
DELETE
都试图获取该行的行更新锁。无论哪个得到它都会继续并删除该行。另一个 DELETE
卡在行锁上,直到获胜的事务提交或回滚。如果它回滚,就好像什么都没发生一样,等待的事务继续正常进行。如果获胜的事务提交删除,等待的事务看到锁已经被释放,并且(在 READ COMMITTED
模式下)重新检查 WHERE 子句谓词以确保该行仍然匹配,发现它不再存在,并携带没有错误,因为删除零行不是错误。在 PL/PgSQL 中,如果要强制执行语句只影响一行,可以检查受影响的行数,如果它与预期的受影响行不匹配,则可以检查
RAISE EXCEPTION
。 INTO STRICT
也有 SELECT
。关于sql - 如何使 PostgreSQL 函数原子化?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/26076416/