【问题标题】:Postgresql - ERROR : tuple concurrently updatedPostgresql - 错误:元组同时更新
【发布时间】:2013-10-15 13:19:08
【问题描述】:

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

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
);

【问题讨论】:

  • @user2511414 使用默认隔离级别

标签: java postgresql concurrency postgresql-9.2


【解决方案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】:

如果 pg 黑客线程可以通过,那么当竞争事务同时更新同一行时,错误就会出现。在您的情况下,这可能是由于 not exists() 子句,它可能会产生同一元组的 true 和两个竞争插入。

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

【讨论】:

  • 两个不同的交易是否有可能从nextval('mystat_sequence')看到相同的值?
  • 它不是,但可能有一些奇怪的事情发生在那里,与 pg_stat_activity 表查找如何与您未突出显示的其他代码交互有关。如果将隔离级别更改为可序列化并不能解决问题,我认为询问 pg 黑客列表是最好的场所; PG 开发人员可能会有更准确的答案,例如postgresql.org/message-id/22416.1116432003@sss.pgh.pa.us
【解决方案3】:

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

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 内部发生了一些事情,但我不知道是什么...

【讨论】:

    【解决方案4】:

    来自 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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-02-11
      • 2021-10-25
      • 2021-01-14
      • 1970-01-01
      相关资源
      最近更新 更多