2014-09-27 53 views
1

假设我有一个像下面的一些PostgreSQL的功能:如何使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; 

从我的理解PostgreSQL的功能insertSthjustDeleteSthjustSelectSth将要自动执行(?)。所以平行执行它们不会搞砸任何东西。

removeSthAfterSelect如果有一个并行执行它可能是SELECT id INTO some_id ..发现的东西,然后同时另一个事务调用justDeleteSthid = someId删除了该行,所以当交易继续这里就不再删除任何东西:DELETE FROM ... WHERE id = some_id;这意味着它混乱了事情。

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

回答

6

交易具有原子承诺的财产,即整个交易保证生效,或者它没有一个生效。

这并不意味着交易不能交互。特别是,在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


但removeSthAfterSelect如果有一个并行执行这可能是因为SELECT INTO ID .. SOME_ID发现的东西,然后同时另一个事务调用justDeleteSth和使用id = someId删除了该行,所以当交易继续它不会删除这里的任何东西:DELETE FROM ... WHERE id = some_id;这意味着它会搅乱一切。

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

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

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

+0

感谢您的回答。在阅读了关于事务隔离之后,我想出了另一个可以在这里找到的问题:http://stackoverflow.com/questions/26195339/select-into-with-select-for-update-in-postgresql。我会很高兴,如果你可以看看:) – insumity 2014-10-04 17:34:01