PostgreSQL 事务重启

标签 postgresql transactions plpgsql

我开始使用 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/

相关文章:

C#-Postgres : I can't open connections when app is running on a shared drive

ruby-on-rails - 在 Rails 5 中存储 Jsonb 的 Postgres 数组意外转义字符串

java - 在 JUnit 中使用 Spring 测试服务时如何回滚数据库事务?

postgresql - 如何在psql中使用外键约束将一个表的结构复制到另一个表?

java - 组织.postgresql.util.PSQLException : ERROR: operator does not exist: integer = bytea

wordpress - 无法使用Docker部署带有Postgres数据库的Wordpress

php - 在事务中使用 MySQL 变量和 PHP 变量

php - 数据库插入随机未使用的行 - 带事务

postgresql - PostgreSQL 中基于游标的记录

postgresql - 更改函数以进行额外的争论/添加参数