Postgresql - 错误:元组同时更新

时间:2013-10-15 13:19:08

标签: java postgresql concurrency postgresql-9.2

以下查询由两个不同用户登录的两个线程同时执行:

WITH raw_stat AS (
    SELECT
       host(client_addr) as client_addr,
       pid ,
       usename
    FROM
       pg_stat_activity
    WHERE
       usename = current_user
)
INSERT INTO my_stat(id, client_addr, pid, usename)
    SELECT
         nextval('mystat_sequence'), t.client_addr, t.pid, t.usename
    FROM (
        SELECT
            client_addr, pid, usename
        FROM
            raw_stat s
        WHERE
            NOT EXISTS (
               SELECT
                  NULL
               FROM
                  my_stat u
               WHERE
                  current_date = u.creation
               AND
                  s.pid = u.pid
               AND
                  s.client_addr = u.client_addr
               AND
                  s.usename = u.usename
            )
    ) t;

我不时会收到以下错误:

tuple concurrently updated

我无法弄清楚抛出此错误的原因以及抛出此错误的原因。你能脱光吗?


这是mystat表的sql定义。

mystats.sql

CREATE TABLE mystat
(
  id bigint NOT NULL,
  creation date NOT NULL DEFAULT current_date,

  client_addr text NOT NULL,
  pid integer NOT NULL,
  usename name NOT NULL,
  CONSTRAINT mystat_pkey PRIMARY KEY (id)
)
WITH (
  OIDS=FALSE
);

4 个答案:

答案 0 :(得分:1)

如果pg黑客线程是任何东西,那么当竞争事务同时更新同一行时,错误就会启动。在你的情况下,它可能是由于not exists()子句,它可能会产生true和两个相同元组的竞争插入。

要解决此问题,您需要使用更强大的锁定(例如谓词锁定),可序列化的隔离级别,或者将所需的逻辑放在upsert语句中(可以使用带有异常块的函数来完成)

答案 1 :(得分:1)

这不是一个真正的答案 - 也许可以帮助那些偶然发现这个错误的人。

在我的情况下,我试图想象并在一个函数中封装所有函数的创建。

这样的东西
CREATE OR REPLACE FUNCTION main_func()
BEGIN

   CREATE OR REPLACE FUNCTION child_func1()
   BEGIN
   END

   CREATE OR REPLACE FUNCTION child_func1()
   BEGIN
   END

   main func stuff...
END

无论出于何种原因,我可以在pgAdmin中调用此函数没问题。而且我可以从Java中调用它 - >> MyBatis的。

然而,当我开始从两个不同的线程调用该函数时,我从OP得到错误:错误:元组同时更新

解决方法是,简单地将这些子函数从主函数中取出,并单独维护它们。

回顾它,由于调用函数而创建函数是一个非常糟糕的主意。但是,我们的想法是将所有功能“封装”在一起。

希望这有助于某人。

答案 2 :(得分:0)

我设法通过将查询更改为此问题来解决我的问题:

INSERT INTO my_stat(id, client_addr, pid, usename)
    SELECT
        nextval('mystat_sequence'), client_addr, pid, usename
    FROM (
        SELECT
           host(client_addr) as client_addr,
           pid ,
           usename
        FROM
           pg_stat_activity
        WHERE
           usename = current_user
    ) s
    WHERE
        NOT EXISTS (
           SELECT
              NULL
           FROM
              my_stat u
           WHERE
              current_date = u.creation
           AND
              s.pid = u.pid
           AND
              s.client_addr = u.client_addr
           AND
              s.usename = u.usename
        );

我觉得Postgresql内部发生了一些事情,但我无法弄清楚是什么......

答案 3 :(得分:0)

来自Postgres的文档(https://www.postgresql.org/docs/current/functions-sequence.html),Because sequences are non-transactional, changes made by setval are not undone if the transaction rolls back

这意味着您需要使用事务自己更新提供线程安全性,因此在事务内运行查询可能会解决您的问题。