我开始玩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,因为要提交的最后一个事务应该重新启动,因为" view"在处理期间balance from account where id=1
发生了变化......
我已经阅读了transaction isolation in official documentation,并且听起来默认read committed
应该正确执行此行为。
我还测试过将隔离级别更改为serializable
,然后最后一次提交的事务确实抛出异常,但我想知道是否有任何"事务重启"功能(正如我所描述的)或者如果我遗漏了某些东西......
答案 0 :(得分:2)
如果对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 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;
这些最近的问题似乎遇到了类似的问题。详细说明和链接: