以下查询由两个不同用户登录的两个线程同时执行:
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
);
答案 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
。
这意味着您需要使用事务自己更新提供线程安全性,因此在事务内运行查询可能会解决您的问题。