PostgreSQL中DISTINCT导致查询性能低下的调优求助
我之前也遇到过类似的PostgreSQL查询性能问题,尤其是DISTINCT导致的耗时暴涨,给你几个亲测有效的优化方向:
这是最有效的优化手段,因为你的需求本质是找到所有符合条件且存在对应候选组的任务记录,而不是把所有关联结果拉出来再去重。EXISTS是半连接逻辑,只要找到匹配的ACT_RU_IDENTITYLINK记录就会停止扫描,不会生成冗余的中间结果,自然不需要DISTINCT去重。
改写后的查询:
SELECT RES.* FROM ACT_RU_TASK RES WHERE RES.ASSIGNEE_ IS NULL AND EXISTS ( SELECT 1 FROM ACT_RU_IDENTITYLINK I WHERE I.TASK_ID_ = RES.ID_ AND I.TYPE_ = 'candidate' AND I.GROUP_ID_ IN ('us1','us2') ) ORDER BY RES.priority_ DESC LIMIT 10;
这个写法应该能把耗时降到和不用DISTINCT时差不多的水平。
你说已经建了索引但没效果,大概率是索引的字段顺序不对。PostgreSQL的复合索引是左前缀匹配的,要把过滤条件里最严格的字段放在前面:
给
ACT_RU_IDENTITYLINK建复合索引:CREATE INDEX idx_identitylink_type_group_task ON ACT_RU_IDENTITYLINK (TYPE_, GROUP_ID_, TASK_ID_);这个索引可以快速过滤出
TYPE_='candidate'且GROUP_ID_在指定列表里的记录,然后直接关联TASK_ID_,不需要全表扫描。给
ACT_RU_TASK建复合索引:CREATE INDEX idx_task_assignee_priority_id ON ACT_RU_TASK (ASSIGNEE_, priority_, ID_);这个索引可以快速筛选出
ASSIGNEE_ IS NULL的任务,同时直接用priority_排序,避免额外的Sort操作,最后通过ID_关联子查询。
如果上面的方法还没解决问题,跑一下EXPLAIN ANALYZE看看具体哪里耗时:
EXPLAIN ANALYZE SELECT DISTINCT RES.* FROM ACT_RU_TASK RES INNER JOIN ACT_RU_IDENTITYLINK I ON I.TASK_ID_ = RES.ID_ WHERE RES.ASSIGNEE_ IS NULL AND I.TYPE_ = 'candidate' AND (I.GROUP_ID_ IN ('us1','us2')) ORDER BY RES.priority_ desc LIMIT 10;
重点看有没有出现Sort(尤其是耗时很长的排序)、Seq Scan(全表扫描)这类操作。如果看到Sort,说明DISTINCT触发了大结果集的排序去重,这就是性能瓶颈,用EXISTS就能解决。
有时候PostgreSQL因为统计信息过时,会选择不好的执行计划。可以手动更新两张表的统计信息:
ANALYZE ACT_RU_TASK; ANALYZE ACT_RU_IDENTITYLINK;
这样应该就能解决你的问题了,我之前用类似的方法把7秒的查询降到了几百毫秒。
内容的提问来源于stack exchange,提问作者Learner

