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 列上有另一个索引。显然,第二个索引没有被使用,我不知道为什么。
执行时间长的原因是
优化器希望通过扫描排序顺序中的所有行并挑选出符合条件的行,来快速找到足够的匹配行,但是执行器必须扫描630835行,直到找到足够的行匹配行。
对于正在检查的每一行,都会执行子选择。
您应该将 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);
我正在使用 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 列上有另一个索引。显然,第二个索引没有被使用,我不知道为什么。
执行时间长的原因是
优化器希望通过扫描排序顺序中的所有行并挑选出符合条件的行,来快速找到足够的匹配行,但是执行器必须扫描630835行,直到找到足够的行匹配行。
对于正在检查的每一行,都会执行子选择。
您应该将 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);