使用VALUES转换查询而不是IN

时间:2014-08-13 08:31:45

标签: sql postgresql database-performance postgresql-9.3

我有以下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进行了测试但是没有用。

1 个答案:

答案 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 UPDATELIMIT子句后应用。这意味着如果行被锁定,当查询在阻塞后重试时,如果它选择与查询相同的行集,则它将在重新检查WHERE子句时消除所有行(因为processing现已设置为1)。所以它将返回零行而不是预期的3000行,或者至少小于3000行。

无论如何:看起来你正在尝试编写一个任务排队系统。不要这样做。你弄错了 - 至少如果你关心碰撞安全,保证只运行一次任务,等等。希望你会通过生成一个只有手的并发/并行任务排队系统来解决它一次完成一大块任务,因此它没有实际的并行性。如果你运气不好,你会错误地生成一个丢失任务的任务排队系统,当它们没有完成时报告完成,无法重试失败的任务等等。

使用现有的任务排队系统 - 将Celery视为一个选项。

BTW,有一个PostgreSQL 9.5补丁添加对FOR UPDATE SKIP LOCKED的支持,这将使得在普通SQL中编写真正的并发任务队列变得容易。它不会成为9.4,所以它不会匆忙提供。