如何使用索引加快Postgres中的排序


10

我正在使用postgres 9.4。

messages具有以下模式:消息属于FEED_ID,并且具有posted_at,还消息可以具有(在答复的情况)的父消息。

                    Table "public.messages"
            Column            |            Type             | Modifiers
------------------------------+-----------------------------+-----------
 message_id                   | character varying(255)      | not null
 feed_id                      | integer                     |
 parent_id                    | character varying(255)      |
 posted_at                    | timestamp without time zone |
 share_count                  | integer                     |
Indexes:
    "messages_pkey" PRIMARY KEY, btree (message_id)
    "index_messages_on_feed_id_posted_at" btree (feed_id, posted_at DESC NULLS LAST)

我想返回按排序的所有消息share_count,但对于每个parent_id,我只想返回一条消息。即,如果多个消息具有相同的parent_id,则仅posted_at返回最新的消息()。在parent_id可以为空,以空消息parent_id都应该回报。

我使用的查询是:

WITH filtered_messages AS (SELECT * 
                           FROM messages
                           WHERE feed_id IN (7) 
                           AND (posted_at >= '2015-01-01 04:00:00.000000') 
                           AND (posted_at < '2015-04-28 04:00:00.000000'))
    SELECT *
    FROM (SELECT DISTINCT ON(COALESCE(parent_id, message_id)) parent_id,
                          message_id, 
                          posted_at, 
                          share_count
          FROM filtered_messages
          ORDER BY COALESCE(parent_id, message_id), posted_at DESC NULLS LAST
         ) messages
    ORDER BY share_count DESC NULLS LAST, posted_at DESC NULLS LAST;

这是http://sqlfiddle.com/#!15/588e5/1/0,在SQL Fiddle中,我已经定义了架构,确切的查询和预期的结果。

但是,一旦message表变大,查询的性能就会变慢。我尝试添加多个排序索引,但似乎没有使用该索引。这是解释:http : //explain.depesz.com/s/Sv2

如何创建正确的索引?


乍一看,ORDER BY子查询中的完全没用。此外,链接的计划不能是已发布查询的结果- metadata例如,没有提及。
dezso

您的描述未涵盖和的作用,feed_id并且posted_at您根本没有提及metadata,这似乎是JSON类型?请修复您的问题以使其一致。您选择CTE中的> 500k行...表中有多少行?您通常在CTE中选择百分之几的行?行的百分比是parent_id IS NULL多少?考虑[postgresql-performance]标记中的信息以获取有关性能的问题。
Erwin Brandstetter

同样重要的是:每行多少行parent_id?(最低/平均/最高)
欧文·布兰德斯特2015年

抱歉,我正试图通过减少一些列来使问题更清楚,share_count实际上在hstore中metadata。目前,消息表中的数据为1000万,但增长很快。我认为要为每个feed_id分成分区表。由于我只提取每个Feed ID。parent_id null与not null的百分比约为60%/ 40%。典型的提取量约为表格的1-2%。(大约10万条消息)100K的性能大约为1s,但是一旦达到500K +,它将使用位图索引,通常需要10s。
翁兆涵

Answers:


9

询问

在任何情况下,此查询都应显着更快:

SELECT parent_id, message_id, posted_at, share_count
FROM   messages
WHERE  feed_id = 7
AND    posted_at >= '2015-01-01 4:0:0'
AND    posted_at <  '2015-04-28 4:0:0'
AND    parent_id IS NULL  -- match index condition
UNION ALL
(
SELECT DISTINCT ON(parent_id)
       parent_id, message_id, posted_at, share_count
FROM   messages
WHERE  feed_id = 7
AND    posted_at >= '2015-01-01 4:0:0'
AND    posted_at <  '2015-04-28 4:0:0'
AND    parent_id IS NOT NULL  -- match index condition
ORDER  BY parent_id, posted_at DESC NULLS LAST
)
ORDER  BY share_count DESC NULLS LAST, posted_at DESC NULLS LAST;
  • CTE在此不执行任何普通子查询也无法交付的操作。CTE引入了优化障碍,因为它是单独执行的,其结果得以实现。

  • 您的子查询级别比实际需要的多。

  • 该表达式(COALESCE(parent_id, message_id)与普通索引不兼容,您需要在该表达式上具有索引。但这也可能不是很有用,取决于数据分布。请点击以下我的链接以获取详细信息。

  • 将简单的案例拆分parent_id IS NULL为单独的案例SELECT可能会或可能不会带来最佳效果。尤其不是,如果无论如何还是很少见,那么在这种情况下结合索引打开的组合查询(COALESCE(parent_id, message_id)可能会更好。其他注意事项也适用...

指标

特别是在这些索引支持下:

CREATE INDEX messages_idx_null ON messages (
  feed_id
, posted_at DESC NULLS LAST
, share_count DESC NULLS LAST
, parent_id, message_id
)
WHERE parent_id IS NULL;

CREATE INDEX messages_idx_notnull ON messages (
  feed_id
, posted_at DESC NULLS LAST
, share_count DESC NULLS LAST
, parent_id, message_id
)
WHERE parent_id IS NOT NULL;

这两个部分索引一起覆盖了整个表并且它们的大小与单个总索引大致相同。

最后两列parent_id, message_id仅在您获得仅索引扫描时才有意义。否则将它们从两个索引中删除。

SQL提琴。

根据缺少的细节,DISTINCT ON可能不是最佳的查询技术。在此处阅读详细说明:

可能还有更快的替代方法:

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.