PostgreSQL - 优化查询性能

PostgreSQL - Optimizing query performance

我正在使用 New Relic 分析我的查询性能,尤其是这个需要很长时间才能完成:

 SELECT  "events".*
 FROM "events"
 WHERE ("events"."deleted_at" IS NULL AND
        "events"."eventable_id" = $? AND
        "events"."eventable_type" = $? OR
        "events"."deleted_at" IS NULL AND
        "events"."eventable_id" IN (SELECT "flow_recipients"."id" FROM "flow_recipients" WHERE "flow_recipients"."contact_id" = $?) AND "events"."eventable_type" = $?)
ORDER BY "events"."created_at" DESC
LIMIT $? OFFSET $? 

有时这个查询需要超过 8 秒才能完成,我不明白为什么。我看了查询解释,但我不确定我是否能理解它:

我的索引有问题吗?有什么我可以优化的吗?我怎样才能进一步调查发生了什么?

我怀疑我使用的是 SELECT 事件。* 而不是只选择我感兴趣的列可能会产生一些影响,但我使用的 LIMIT 为 15,所以我我不确定它会影响那么大。

[编辑] 我在 created_at 列上有一个索引,在 eventable_id 和 eventable_type 列上有另一个索引。显然,第二个索引没有被使用,我不知道为什么。

执行时间长的原因是

  1. 优化器希望通过扫描排序顺序中的所有行并挑选出符合条件的行,来快速找到足够的匹配行,但是执行器必须扫描630835行,直到找到足够的行匹配行。

  2. 对于正在检查的每一行,都会执行子选择。

您应该将 OR 重写为 UNION:

SELECT * FROM events
WHERE deleted_at IS NULL
  AND eventable_id = $?
  AND eventable_type = $?
UNION
SELECT * FROM events e
WHERE deleted_at IS NULL
  AND eventable_type = $?
  AND EXISTS (SELECT 1
              FROM flow_recipients f
              WHERE f.id = e.eventable_id
                AND f.contact_id = $?);

如果 events 有一个主键,这个查询会做同样的事情。

有用的索引取决于选择的执行计划,但这些可能是好的:

CREATE INDEX ON events (eventable_type, eventable_id)
   WHERE deleted_at IS NULL;

CREATE INDEX ON flow_recipients (contact_id);