在postgres中优化大的“不同”选择

时间:2014-09-23 22:27:11

标签: sql postgresql

我有一个相当大的数据集(数百万行)。我在向某个查询引入“独特”概念时遇到了麻烦。 (我在引号中加上不同的内容,因为这可以通过posgtres关键字DISTINCT或“group by”形式提供)。

非独特的搜索需要1ms - 2ms;所有尝试引入“独特”概念的尝试都已达到50,000ms-90,000ms范围。

我的目标是根据事件流中最近的外观显示最新资源。

我的非独特查询基本上是这样的:

SELECT
    resource.id AS resource_id,
    stream_event.event_timestamp AS event_timestamp
FROM
    resource
    JOIN
        resource_2_stream_event ON (resource.id = resource_2_stream_event.resource_id)
    JOIN
        stream_event ON (resource_2_stream_event.stream_event_id = stream_event.id)
WHERE
    stream_event.viewer = 47
ORDER BY event_timestamp DESC
LIMIT 25
;

我使用DISTINCT,GROUP BY和MAX(event_timestamp)尝试了许多不同形式的查询(和子查询)。问题不在于获得有效的查询,而是在合理的执行时间内运行。查看每个的EXPLAIN ANALYZE输出,一切都在索引之前运行。问题似乎是,任何尝试重复删除我的结果,postges必须将整个结果集组装到磁盘上;由于每个表有数百万行,这就成了瓶颈。

-

更新

这是一个工作组 - 查询:

EXPLAIN ANALYZE 
SELECT
    resource.id AS resource_id,
    max(stream_event.event_timestamp) AS stream_event_event_timestamp
FROM 
    resource 
    JOIN resource_2_stream_event ON (resource_2_stream_event.resource_id = resource.id) 
    JOIN stream_event ON stream_event.id = resource_2_stream_event.stream_event_id
WHERE (
        (stream_event.viewer_id = 57) AND 
        (resource.condition_1 IS NOT True) AND 
        (resource.condition_2 IS NOT True) AND 
        (resource.condition_3 IS NOT True) AND 
        (resource.condition_4 IS NOT True) AND 
        ( 
            (resource.condition_5 IS NULL) OR (resource.condition_6 IS NULL) 
        )
    )
GROUP BY (resource.id)
ORDER BY stream_event_event_timestamp DESC LIMIT 25;

查看查询规划器(通过EXPLAIN ANALYZE),似乎添加max + groupby子句(或不同的)会强制执行顺序扫描。这花费了大约一半的时间来电脑。已经有一个包含每个“条件”的索引,我尝试创建一组索引(每个元素一个)。没有工作。

无论如何,差异在2ms到72,000ms之间

1 个答案:

答案 0 :(得分:2)

通常,distinct on是获得每行一行的最有效方法。我建议尝试:

SELECT DISTINCT ON (r.id) r.id AS resource_id, se.event_timestamp
FROM resource r JOIN
     resource_2_stream_event r2se
     ON r.id = r2se.resource_id JOIN
     stream_event se
     ON r2se.stream_event_id = se.id
WHERE se.viewer = 47
ORDER BY r.id, se.event_timestamp DESC
LIMIT 25;

resource(id, event_timestamp)上的索引可能有助于提升效果。

编辑:

您可以尝试使用CTE来获得所需内容:

WITH CTE as (
      SELECT r.id AS resource_id,
             se.event_timestamp AS stream_event_event_timestamp
      FROM resource r JOIN
           resource_2_stream_event r2se
           ON r2se.resource_id = r.id JOIN
           stream_event se
           ON se.id = r2se.stream_event_id
      WHERE ((se.viewer_id = 57) AND 
             (r.condition_1 IS NOT True) AND 
             (r.condition_2 IS NOT True) AND 
             (r.condition_3 IS NOT True) AND 
             (r.condition_4 IS NOT True) AND 
             ( (r.condition_5 IS NULL) OR (r.condition_6 IS NULL) 
             )
            )
    )
SELECT resource_id, max(stream_event_event_timestamp) as stream_event_event_timestamp
FROM CTE
GROUP BY resource_id
ORDER BY stream_event_event_timestamp DESC
LIMIT 25;

Postgres实现了CTE。因此,如果没有那么多匹配,这可以通过使用CTE的索引来加速查询。