como usar o índice para acelerar a classificação no postgres

10

Estou usando o postgres 9.4.

O messagesesquema tem o seguinte: messages pertence a feed_id e postou_at, também as mensagens podem ter uma mensagem pai (no caso de respostas).

                    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)

Quero retornar todas as mensagens ordenadas por share_count, mas para cada uma parent_id, quero retornar apenas uma mensagem. ou seja, se várias mensagens tiverem o mesmo parent_id, somente a última ( posted_at) será retornada. O parent_idpode ser nulo, todas as mensagens com nulo parent_iddevem retornar.

A consulta que usei é:

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;

Aqui está o http://sqlfiddle.com/#!15/588e5/1/0 , no SQL Fiddle, eu defini o esquema, a consulta exata e o resultado esperado.

Mas o desempenho da consulta é lento quando a tabela de mensagens aumenta. Tentei adicionar vários índices de classificação, mas ele não parece usar o índice. Aqui está a explicação: http://explain.depesz.com/s/Sv2

Como posso criar um índice correto?

Zhaohan Weng
fonte
À primeira vista, ORDER BYa subconsulta é totalmente inútil. Além disso, o plano vinculado não pode ser resultado da consulta lançada - não há menção metadata, por exemplo.
dezso
Sua descrição não cobre o papel de feed_ide posted_atvocê não mencionou metadatanada, o que parece ser do tipo JSON? Repare sua pergunta para torná-la consistente. Você seleciona> 500k linhas na CTE ... Quantas linhas estão na tabela? Qual porcentagem de linhas você normalmente seleciona na CTE? Qual a porcentagem de linhas parent_id IS NULL? Considere as informações na tag [postgresql-performance] para perguntas sobre desempenho.
Erwin Brandstetter
Também importante: quantas linhas para cada parent_id? (min / avg / max)
Erwin Brandstetter
desculpe, eu estava tentando deixar a pergunta mais clara, reduzindo algumas das colunas, o share_count estava realmente no hstore metadata. Atualmente, a tabela de mensagens possui 10 mil dados, mas aumenta rapidamente. Eu acho que para separar em tabelas de partição para cada feed_id. Como estou buscando apenas por ID do feed. a porcentagem de parent_id null vs not null é de cerca de 60% / 40%. uma busca típica é de cerca de 1-2% da tabela. (em torno de 100 mil mensagens) O desempenho para 100 mil é de cerca de 1s, mas quando chega a 500 mil +, ele usa o índice de bitmap e normalmente leva 10 segundos.
Zhaohan Weng

Respostas:

9

Inquerir

Esta consulta deve ser substancialmente mais rápida em qualquer caso:

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;
  • O CTE não faz nada aqui que uma subconsulta simples também não possa fornecer. E um CTE introduz uma barreira de otimização, pois é executado separadamente e seu resultado é materializado.

  • Você tem mais um nível de subconsulta do que realmente precisa.

  • A expressão (COALESCE(parent_id, message_id)não é compatível com um índice simples, você precisaria de um índice nessa expressão. Mas isso também pode não ser muito útil, dependendo da distribuição dos dados. Siga meus links abaixo para obter informações detalhadas.

  • Dividir o caso simples de parent_id IS NULLum em separado SELECTpode ou não oferecer o melhor. Especialmente, se esse for um caso raro de qualquer maneira, nesse caso, uma consulta combinada com um índice ativado (COALESCE(parent_id, message_id)poderá ter um desempenho melhor. Outras considerações se aplicam ...

Índices

Especialmente quando suportado com estes índices:

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;

Os dois índices parciais cobrem a tabela inteira juntos e têm o mesmo tamanho juntos que um único índice total.

As duas últimas colunas parent_id, message_idsó fazem sentido se você conseguir verificações somente de índice . Caso contrário, remova-os dos dois índices.

SQL Fiddle.

Dependendo dos detalhes ausentes, DISTINCT ONpode ou não ser a melhor técnica de consulta para esse fim. Leia aqui a explicação detalhada:

E alternativas possivelmente mais rápidas aqui:

Erwin Brandstetter
fonte