假设我有一些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
应该原子地执行?
答案 0 :(得分:8)
一个事务具有原子 commit 的属性,即整个事务保证生效,或者没有一个事件生效。
这并不意味着交易无法互动。特别是,在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。
但是对于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 STRICT
还有SELECT
。
答案 1 :(得分:0)
通常可以使用锁定来实现所需的“原子”行为
例如:
BEGIN; -- transaction
SELECT pg_advisory_xact_lock(123); -- 123 is any big integer
-- do your "atomic" stuff here, other transactions
-- trying to acquire the same (123) lock will be waiting for it to be released
COMMIT; -- transaction has ended, the locks are released automatically
缺点是这种锁定的块不会并行执行。有关详细信息,请参阅文档https://www.postgresql.org/docs/11/explicit-locking.html。