我有以下SQL查询
UPDATE user
SET processing = 1
WHERE
ID IN (
SELECT
ID
FROM
user
WHERE
processing =0
LIMIT 3000 FOR UPDATE
) RETURNING *
我试图在thread之后对其进行优化
UPDATE user
SET processing = 1
WHERE
ID = ANY (ARRAY (
SELECT
ID
FROM
user
WHERE
processing =0
LIMIT 3000 FOR UPDATE
)) RETURNING *
它工作正常。
现在我也想测试VALUES,但我没有找到正确的方法来做... 有人知道该怎么办?我已经使用string_agg进行了测试但是没有用。
答案 0 :(得分:1)
VALUES
子句在这里没用。作为@a_horse_with_no_name注释,它仅适用于静态(文字)数据。您正在使用查询来按需生成数据。
IN
应由优化程序转换为= ANY
;我不希望有任何差别。
在这两种情况下,您都在搜索3000个元素数组。对于高行计数而言,这并不是非常有效。我通常会在FROM
中的子查询中使用联接。
UPDATE user
SET processing = 1
FROM
(
SELECT
ID
FROM
user
WHERE
processing = 0
LIMIT 3000 FOR UPDATE
) l
WHERE l.id = user.id
RETURNING *
(未测试的)。
但这不会解决查询逻辑的潜在问题。
您未在ORDER BY
中指定任何SELECT ... FOR UPDATE
。因此,您将以任何可能的顺序获得服务器决定为您提供的任何行。如果任何行已被锁定,则查询将阻止并重试。
FOR UPDATE
在 LIMIT
子句后应用。这意味着如果行被锁定,当查询在阻塞后重试时,如果它选择与查询相同的行集,则它将在重新检查WHERE
子句时消除所有行(因为processing
现已设置为1
)。所以它将返回零行而不是预期的3000行,或者至少小于3000行。
无论如何:看起来你正在尝试编写一个任务排队系统。不要这样做。你将弄错了 - 至少如果你关心碰撞安全,保证只运行一次任务,等等。希望你会通过生成一个只有手的并发/并行任务排队系统来解决它一次完成一大块任务,因此它没有实际的并行性。如果你运气不好,你会错误地生成一个丢失任务的任务排队系统,当它们没有完成时报告完成,无法重试失败的任务等等。
使用现有的任务排队系统 - 将Celery视为一个选项。
BTW,有一个PostgreSQL 9.5补丁添加对FOR UPDATE SKIP LOCKED
的支持,这将使得在普通SQL中编写真正的并发任务队列变得容易。它不会成为9.4,所以它不会匆忙提供。