2015-03-31 74 views
2

我开始在PostgreSQL玩,发现sequence从来没有回退,甚至失败INSERT
我读过,这是预期,以防止并发事务的重复序列,我发现奇怪的是我的数据库的经验是只有GTM此时事务重启是常见的,这正是使用。PostgreSQL的交易重新启动

所以我想测试在PGSQL重新启动,并在数据库中装载这样的:

CREATE SEQUENCE account_id_seq; 

CREATE TABLE account 
(
    id integer NOT NULL DEFAULT nextval('account_id_seq'), 
    title character varying(40) NOT NULL, 
    balance integer NOT NULL DEFAULT 0, 
    CONSTRAINT account_pkey PRIMARY KEY (id) 
); 

INSERT INTO account (title) VALUES ('Test Account'); 

CREATE OR REPLACE FUNCTION mytest() RETURNS integer AS $$ 
DECLARE 
    cc integer; 
BEGIN 
    cc := balance from account where id=1; 

    RAISE NOTICE 'Balance: %', cc; 
    perform pg_sleep(3); 

    update account set balance = cc+10 where id=1 RETURNING balance INTO cc; 

    return cc; 
END 
$$ 
LANGUAGE plpgsql; 

因此,功能mytest()将检索的平衡,等待3秒(让我启动其他程序),然后更新余额基于已保存的变量。

我现在从外壳推出2次调用该函数直接:

void$ psql -c "select * from account where id=1" 
id | title  | balance 
----+--------------+--------- 
    1 | Test Account |  0 
(1 row) 

void$ psql -c "select mytest()" & PIDA=$! && psql -c "select mytest()" && wait $PIDA 
[1] 3312 
NOTICE: Balance: 0 
NOTICE: Balance: 0 
mytest 
-------- 
    10 
(1 row) 

mytest 
-------- 
    10 
(1 row) 

[1]+ Done     psql -c "select mytest()" 
void$ psql -c "select * from account where id=1" 
id | title  | balance 
----+--------------+--------- 
    1 | Test Account |  10 
(1 row) 

我希望平衡是20,不是10,作为最后的交易将被提交应重新启动作为的balance from account where id=1“视图”在加工过程中改变...

我读过有关transaction isolation in official documentation和它的声音对我说默认read committed应该强制执行这种行为恰恰..
我也测试了改变隔离级别serializable,然后最后TRAN saction承诺不抛出一个异常,但我想知道,如果没有任何“交易重启”功能(如我所描述的),或者如果我失去了一些东西......

回答

2

你得到一个“重启”如果自动您使用row level locks进行适当的查询。

CREATE OR REPLACE FUNCTION mytest() 
    RETURNS integer AS 
$func$ 
DECLARE 
    cc integer; 
BEGIN 
    SELECT INTO cc balance FROM account WHERE id = 1 FOR UPDATE; 

    RAISE NOTICE 'Balance: %', cc; 
    PERFORM pg_sleep(3); 

    UPDATE account SET balance = cc+10 
    WHERE id = 1 
    RETURNING balance 
    INTO cc; 

    RETURN cc; 
END 
$func$ LANGUAGE plpgsql;

SELECT ... FOR UPDATE需要一个行级锁宣布的说法,此行是要:准确地说,本次交易不会重新启动作为一个整体,它试图在​​锁定行时只是等待轮到被更新。同样的功能要在其他交易同样会被阻塞,等到第一个提交或回滚 - 然后采取锁定本身并建立在对更新的行,让实验的结果将是20,而不是10

你可以用一个简单明了的UPDATE查询自动采取适当FOR UPDATE locks更有效地具有相同的

UPDATE account 
SET balance = balance + 10 
WHERE id = 1 
RETURNING balance; 

这些问题最近似乎遇到了类似的问题。详细的说明和链接:

+0

锁是不完全一样的,因为我想/描述的行为,但它是一个替代方案,我也不知道如何使用和适用于我的情况,因此被接受为答案,谢谢!我知道我可以把它简化成一个单一的更新,但目的是为了方便地正是测试,看它是否会“重新启动”。 – 2015-03-31 14:19:02

+0

对于“重启”,我的意思是第二个事务不会等待第一个完成(就像第一行的LOCK),但它会执行,并且只在COMMIT部分检查“原始数据库视图”是否更改以及结果将是意外的,因此从START TRANSACTION点重新开始。 – 2015-03-31 14:21:53

+0

将事务隔离级别设置为“可序列化”是否会执行此操作,但不会重新启动会引发异常。我想我可以有一个outter函数处理这个异常,它会重新执行内部函数,直到它没有抛出它,但是暂时LOCKs工作,甚至听起来像是一个更适合我的场景的解决方案。 – 2015-03-31 14:23:46