Suponha que eu tenha uma tabela que tenha uma descrição como:
create table my_table (
id serial,
create_date timestamp with time zone default now(),
data text
);
e uma consulta como:
select * from my_table
where create_date >= timestamp with time zone 'yesterday'
Qual índice será teoricamente mais rápido e por quê?
create index index_a on my_table (create_date);
create index index_b on my_table (create_date DESC);
Não gosto do nome
"create_date"para uma coluna que não é realmente uma,date
mas umatimestamptz
. Usando "created_at" em vez disso.Como
created_at
pode serNULL
, esta 3ª variante será mais rápida (mesmo que não muito):NULL
valores são classificados após o maior valor por padrão.DESCENDING
A ordem de classificação é a inversão perfeita, portanto, osNULL
valores vêm primeiro. Ver:O Postgres pode escanear índices de árvore B para trás quase na mesma velocidade, então ambas as suas variantes estão quase no mesmo nível. Mas o operador
>=
excluiNULL
valores (como a maioria dos operadores). Portanto, o Postgres precisa pular osNULL
valores iniciais / finais, respectivamente, primeiro. Normalmente não é caro, mas ainda assim.O índice com
DESC NULLS LAST
(ouNULLS FIRST
) tem os maiores valores primeiro e osNULL
valores por último (ou vice-versa), então a consulta pode começar a ler diretamente do topo (abaixo) do índice.Se não pode haver
NULL
valores, não haverá diferença perceptível. E você deve declarar a colunaNOT NULL
. (E você deveria ter dito isso.)Se as inserções vierem com timestamps estritamente ascendentes (e não houver atualizações!) - ou se isso for verdade para linhas inseridas recentemente desde "ontem", as linhas (relevantes) são fisicamente agrupadas por timestamp automaticamente. Caso contrário, pode valer a pena agrupar fisicamente as linhas de tempos em tempos. (Sem interferir no carregamento simultâneo no banco de dados!) Isso pode fazer uma diferença maior, pois mantém o número de páginas de dados que precisam ser lidas no mínimo. Ver:
Se sua mesa for grande, um índice parcial pode pagar:
Ele corta a maioria das linhas antigas, para que o índice diminua para uma fração de tamanho.
Mas como seu cut-off (
'yesterday'
) é um alvo móvel, você terá que recriar esse índice de tempos em tempos para remover tuplas antigas, ou o benefício se deteriorará com o tempo. Tipo, diariamente, semanalmente, mensalmente - você decide.Com o cache quente, esse índice parcial não será muito mais rápido que o índice completo, mas como é muito menor, suas chances de permanecer no cache são maiores (depende da sua configuração completa), o que normalmente faz uma grande diferença. (E não ocupa tantos recursos para começar.)
Como temos um índice tão pequeno agora, e enquanto lidamos com poucas colunas (ou você não precisa realmente
SELECT *
começar?!), podemos também torná-lo um índice de cobertura (Postgres 11 ou posterior):Novamente, os detalhes dependem da situação completa. Relacionado:
Se algumas pré-condições forem atendidas, você obterá varreduras somente de índice mais baratas agora. A ordem física das linhas na tabela não importa neste caso.
Ah, e mova essa
timestamptz
coluna para uma posição diferente na definição da tabela. A maneira como você o tem agora maximiza o inchaço devido ao preenchimento de alinhamento. Qualquer outra posição para atimestamptz
coluna é melhor. Curti:Ver: