PostgreSQL事务重启

PostgreSQL事务重启,postgresql,transactions,plpgsql,Postgresql,Transactions,Plpgsql,我开始使用PostgreSQL,并注意到,sequences从不回滚,即使在失败的INSERT 我已经读到,正如预期的那样,它可以防止并发事务上的重复序列,我发现这很奇怪,因为我的数据库经验仅限于事务重启是常见的,并且正是用于此目的 所以我想在PGSQL中测试重启,并将其加载到数据库中: CREATE SEQUENCE account_id_seq; CREATE TABLE account ( id integer NOT NULL DEFAULT nextval('account_id

我开始使用PostgreSQL,并注意到,
sequence
s从不回滚,即使在失败的
INSERT

我已经读到,正如预期的那样,它可以防止并发事务上的重复序列,我发现这很奇怪,因为我的数据库经验仅限于事务重启是常见的,并且正是用于此目的

所以我想在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()
将检索balance,等待3秒钟(让我启动另一个进程),然后根据保存的变量更新balance

现在,我直接从shell对此函数启动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,因为在处理过程中,
账户余额(id=1
的“视图”发生变化时,提交的最后一笔交易应该重新启动

我读过,我觉得默认的
readcommitted
应该严格执行此行为。
我还测试了将隔离级别更改为可序列化,然后提交的最后一个事务确实会引发异常,但我想知道是否没有任何“事务重新启动”功能(如我所述),或者是否缺少某些内容…

如果使用适当的查询,您会自动获得“重新启动”。准确地说,事务不会作为一个整体重新启动,它只是在尝试锁定中的行时等待轮到它:

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


锁与我想要/描述的行为并不完全相同,但这是一个我也不知道如何在我的案例中使用和工作的替代方案,因此被接受为答案,谢谢!我知道我可以将它简化为一个更新,但目的是为了方便地测试它是否会“重新启动”。使用“重新启动”我的意思是,第二个事务不会等待第一个事务完成(就像第一行上的锁),但它会执行,并且只在提交部分检查“原始DB视图”已更改,结果将是意外的,因此从开始事务点重新启动。将事务隔离级别设置为“serializable”会执行此检查,但不会重新启动,而是会引发异常。我想我可以让一个outter函数以某种方式处理该异常,它将重新执行内部函数,直到它没有抛出该异常为止,但就目前而言,锁可以工作,甚至听起来更适合我的场景。可序列化隔离,以备将来参考,以防任何人严格地出现在这里,PostgreSQL不会像Oracle(例如)在
READ COMMITTED
中遇到锁时那样重新启动查询。相反,它只重新读取锁定的行并继续。请参阅源代码中的
EvalPlanQual
,了解一些血淋淋的细节。因此,您实际上可以获得基于重启的方法无法实现的异常。
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;
UPDATE account
SET    balance = balance + 10
WHERE  id = 1
RETURNING  balance;