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

4 回答 4

3

这并不是一个真正的答案 - 可能会帮助其他偶然发现此错误的人。

就我而言,我试图将所有函数的创建封装在一个函数中。

就像是

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 收到错误:错误:元组同时更新

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

回想起来,通过调用函数来创建函数是一个非常糟糕的主意。然而,这个想法是将所有功能“封装”在一起。

希望这可以帮助某人。

于 2014-12-05T22:15:00.747 回答
1

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

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

于 2013-10-16T10:30:38.053 回答
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 内部发生了一些事情,但我不知道是什么......

于 2013-10-17T09:51:41.470 回答
0

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

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

于 2020-07-08T08:59:30.590 回答