我开始使用 PostgreSQL,并注意到 sequence
永远不会回滚,即使 INSERT
失败也是如此。
我读到它正如预期的那样可以防止并发事务中的重复序列,并且我发现这很奇怪,因为我的数据库经验仅适用于 GTM事务重新启动很常见,并且正是用于此目的。
所以我想在 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 秒(让我启动另一个进程),然后根据保存的变量更新余额。
我现在直接从 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 的帐户余额的“ View ”在处理过程中发生了变化
...
我读过transaction isolation in official documentation在我看来,默认的已提交
应该精确地强制执行此行为..
我还测试了将隔离级别更改为可序列化,然后提交的最后一个事务确实抛出异常,但我想知道是否没有任何“事务重新启动”功能(正如我所描述的)或者如果我遗漏了什么......
最佳答案
如果您使用 row level locks 的正确查询,您会自动“重新启动” 。准确地说,事务并没有整体重新启动,它只是在尝试锁定 default transaction isolation READ COMMITTED
中的一行时等待轮到它。 :
CREATE OR REPLACE FUNCTION mytest()
RETURNS integer AS
$func$
DECLARE
cc integer;
BEGIN
SELECT INTO cc balance FROM account WHERE id = 1 <b>FOR UPDATE</b>;
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。
通过使用适当的 FOR UPDATE
locks 的简单明了的 UPDATE
查询,您可以更有效地获得相同的更多。自动:
UPDATE account
SET balance = balance + 10
WHERE id = 1
RETURNING balance;
最近的这些问题似乎遇到了类似的问题。详细解释及链接:
关于PostgreSQL 事务重启,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29366621/