【问题标题】:PostgreSQL generic handler for serialization failure用于序列化失败的 PostgreSQL 通用处理程序
【发布时间】:2015-03-31 15:17:35
【问题描述】:

这是one 的后续问题,所以我知道我可以使用(阻塞)LOCK,但我想使用谓词锁和可序列化事务隔离。

我想要的是一个通用的序列化失败处理程序,它将重试函数/查询 X 次。

例如,我有这个:

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;

CREATE OR REPLACE FUNCTION myretest() RETURNS integer AS $$
DECLARE
    tries integer := 5;
BEGIN
    WHILE TRUE LOOP
        BEGIN -- nested block for exception
            RETURN mytest();
        EXCEPTION
            WHEN SQLSTATE '40001' THEN
                IF tries > 0 THEN
                    tries := tries - 1;
                    RAISE NOTICE 'Restart! % left', tries;
                ELSE
                    RAISE EXCEPTION 'NO RESTARTS LEFT';
                END IF;
        END;
    END LOOP;
END
$$
LANGUAGE plpgsql;

因此,如果同时直接调用mytest(),我会在最后一次提交时遇到序列化失败:

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

ERROR:  could not serialize access due to concurrent update
CONTEXT:  SQL statement "update account set balance = cc+10 where id=1 RETURNING balance"
PL/pgSQL function mytest() line 10 at SQL statement

如果我调用 myretest(),它应该尝试执行 mytest(),直到第 5 次尝试它会引发异常。

所以我在这里有两点(也许点 2 也使点 1 无效):

  • myretest() 无法按预期工作,即使在并发线程完成后,每次迭代都会导致 serialiation_failure 异常:我应该添加什么来“重置”事务吗?

  • 我怎样才能使这个(myretest() 逻辑)通用,以便它适用于系统中的每个调用函数,而无需“包装”函数本身?

【问题讨论】:

    标签: postgresql isolation-level transaction-isolation


    【解决方案1】:

    只要您使用某种框架,当事务收到带有SQLSTATE4000140P01 的错误时,可序列化事务就可以提供您正在寻找的内容。

    在 PostgreSQL 中,函数总是在事务的上下文中运行。您不能在“包装器”函数的上下文中启动新事务。这将需要一个稍微不同的功能,通常称为“存储过程”——PostgreSQL 中不存在的东西。因此,您需要将管理重启的逻辑放入将事务提交到数据库的代码中。幸运的是,有许多连接器——Java、perl、python、tcl、ODBC 等。甚至还有一个连接器用于在 PostgreSQL 过程语言中与 PostgreSQL 数据库建立单独的连接,这可能允许您执行类似的操作你想要什么:

    http://www.postgresql.org/docs/current/static/dblink.html

    我已经在各种“客户端”框架中看到了这一点。显然,将其传播到应用程序在逻辑上处理数据库的所有位置是一个坏主意,但是有很多充分的理由通过一个“访问器”方法(或至少其中的一小部分)路由所有数据库请求),并且大多数框架都提供了一种在该层处理此问题的方法。 (例如,在 Spring 中,您可能希望使用依赖注入创建事务管理器。)这可能属于您用于应用程序逻辑的某种语言,但如果您真的想要,您可能会使用 plpgsql 和 dblink;不过,这可能不是您最简单的方法。

    【讨论】:

      猜你喜欢
      • 2011-07-15
      • 1970-01-01
      • 2011-07-11
      • 1970-01-01
      • 2020-10-15
      • 1970-01-01
      • 2015-11-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多