Esta questão está relacionada a uma anterior que fiz: Ordem das colunas em um índice composto no PostgreSQL (e ordem da consulta)
Em vez de sobrecarregar essa pergunta, acho que posso aguçar e limitar minha pergunta aqui. Dada a seguinte consulta (e EXPLAIN ANALYZE), o índice composto que estou criando está ajudando?
Esta primeira consulta foi executada apenas com índices simples (um GIST no esboço) e um (BTREE no pid).
A consulta é:
EXPLAIN ANALYZE SELECT DISTINCT ON (path) oid, pid, product_name, type, path, size
FROM portal.inventory AS inv
WHERE ST_Intersects(st_geogfromtext('SRID=4326;POLYGON((21.51947021484375 51.55059814453125, 18.9129638671875 51.55059814453125, 18.9129638671875 48.8287353515625, 21.51947021484375 48.8287353515625, 21.51947021484375 51.55059814453125))'), inv.outline)
AND (inv.pid in (20010,20046))
--
O resultado foi o seguinte (que é mais rápido, mas talvez seja apenas porque o banco de dados estava quente).
"Unique (cost=581.76..581.76 rows=1 width=89) (actual time=110.436..110.655 rows=249 loops=1)"
" -> Sort (cost=581.76..581.76 rows=1 width=89) (actual time=110.434..110.477 rows=1377 loops=1)"
" Sort Key: path"
" Sort Method: quicksort Memory: 242kB"
" -> Bitmap Heap Scan on inventory inv (cost=577.48..581.75 rows=1 width=89) (actual time=39.257..105.878 rows=1377 loops=1)"
" Recheck Cond: ((pid = ANY ('{20010,20046}'::integer[])) AND ('0103000020E6100000010000000500000000000000FC843540000000007AC6494000000000B8E93240000000007AC6494000000000B8E9324000000000146A484000000000FC84354000000000146A484000000000FC843540000000007AC64940'::geography && outline))"
" Rows Removed by Index Recheck: 3731"
" Filter: (_st_distance('0103000020E6100000010000000500000000000000FC843540000000007AC6494000000000B8E93240000000007AC6494000000000B8E9324000000000146A484000000000FC84354000000000146A484000000000FC843540000000007AC64940'::geography, outline, 0::double precision, false) < 1e-005::double precision)"
" Rows Removed by Filter: 533"
" -> BitmapAnd (cost=577.48..577.48 rows=1 width=0) (actual time=38.972..38.972 rows=0 loops=1)"
" -> Bitmap Index Scan on inventory_pid_idx (cost=0.00..123.82 rows=6204 width=0) (actual time=1.116..1.116 rows=7836 loops=1)"
" Index Cond: (pid = ANY ('{20010,20046}'::integer[]))"
" -> Bitmap Index Scan on inventory_outline_idx (cost=0.00..453.41 rows=8212 width=0) (actual time=37.765..37.765 rows=63112 loops=1)"
" Index Cond: ('0103000020E6100000010000000500000000000000FC843540000000007AC6494000000000B8E93240000000007AC6494000000000B8E9324000000000146A484000000000FC84354000000000146A484000000000FC843540000000007AC64940'::geography && outline)"
"Total runtime: 110.731 ms"
Agora aqui está o resultado com o índice composto adicionado: (observe que o tempo absoluto foi mais lento)
"Unique (cost=37.81..37.82 rows=1 width=89) (actual time=2464.353..2464.561 rows=249 loops=1)"
" -> Sort (cost=37.81..37.82 rows=1 width=89) (actual time=2464.349..2464.389 rows=1377 loops=1)"
" Sort Key: path"
" Sort Method: quicksort Memory: 242kB"
" -> Bitmap Heap Scan on inventory inv (cost=33.54..37.80 rows=1 width=89) (actual time=2361.018..2459.653 rows=1377 loops=1)"
" Recheck Cond: (('0103000020E6100000010000000500000000000000FC843540000000007AC6494000000000B8E93240000000007AC6494000000000B8E9324000000000146A484000000000FC84354000000000146A484000000000FC843540000000007AC64940'::geography && outline) AND (pid = ANY ('{20010,20046}'::integer[])))"
" Filter: (_st_distance('0103000020E6100000010000000500000000000000FC843540000000007AC6494000000000B8E93240000000007AC6494000000000B8E9324000000000146A484000000000FC84354000000000146A484000000000FC843540000000007AC64940'::geography, outline, 0::double precision, false) < 1e-005::double precision)"
" Rows Removed by Filter: 533"
" -> Bitmap Index Scan on inventory_compound_idx (cost=0.00..33.53 rows=1 width=0) (actual time=2321.684..2321.684 rows=1910 loops=1)"
" Index Cond: (('0103000020E6100000010000000500000000000000FC843540000000007AC6494000000000B8E93240000000007AC6494000000000B8E9324000000000146A484000000000FC84354000000000146A484000000000FC843540000000007AC64940'::geography && outline) AND (pid = ANY ('{20010,20046}'::integer[])))"
"Total runtime: 2558.022 ms"
Finalmente, aqui está a definição da tabela:
CREATE TABLE portal.inventory
(
oid bigint,
product_name character varying(100),
type character varying(25),
pid integer,
size bigint,
date timestamp without time zone,
path character varying(200),
outline geography(Polygon,4326)
)
WITH (
OIDS=FALSE
);
CREATE INDEX inventory_compound_idx
ON portal.inventory
USING gist
(outline, pid);
CREATE INDEX inventory_outline_idx
ON portal.inventory
USING gist
(outline);
CREATE INDEX inventory_pid_idx
ON portal.inventory
USING btree
(pid);
ATUALIZAÇÃO: Respostas às perguntas listadas abaixo:
Posso adaptar a tabela, mas estou tentando manter as linhas finas. Suas sugestões são variadas, tipos, etc. são coisas que eu gostaria de mudar.
Basicamente, cada linha representa um pouco de metadados sobre um arquivo de imagem geoespacial. Estamos administrando 50 milhões, e isso pode crescer para centenas de milhões ou mais. No banco de dados, cada arquivo é referenciado por um OID exclusivo (desculpe pela duplicação do termo). Eles são agrupados por "produtos" onde PID é o ID do produto. Pode haver cerca de 1.000 OIDs por produto. Cada arquivo de imagem possui uma caixa delimitadora geoespacial (o contorno). Isso é realmente tudo o que preciso para pesquisar. O restante dos dados não será nulo (tipo é uma string de texto, tamanho é o tamanho do arquivo, data é a data em que o arquivo foi criado e caminho é um caminho de arquivo UNC para o arquivo).
Agora, aqui está o motivo pelo qual ordenei a consulta por esboço e, em seguida, PID. Os produtos serão agrupados geoespacialmente. Portanto, todas as linhas OID para Cracóvia, Polônia, estarão localizadas fisicamente na mesma região. Portanto, suponho que, se eu reduzir o balde para uma região pequena, o segundo índice será bem pequeno (digamos, cerca de 100 produtos para uma região da cidade). Que a cláusula IN( ..) retirará.
Os valores reais do PIDS foram retirados da outra pergunta que postei aqui. Mas essa tabela é apenas para produtos e, portanto, seu tamanho é de cerca de 30K, o que significa pesquisas rápidas e sem necessidade de consultas compostas.
Eu me pergunto se o planejador POSTGreSQL é inteligente o suficiente para decidir se um índice composto por (contorno, pid) é mais rápido que (pid, esboço) se ambos os índices estiverem lá. Bem, acho que posso testar.
Em um índice GiST, a ordem das colunas tem um significado diferente do que em um índice B-tree . Por documentação:
Resumindo: coloque as colunas mais seletivas primeiro.
Sua
EXPLAIN
saída mostra que a condição ativadapid
é mais seletiva (rows=7836
) do que a ativadaoutline
(rows=63112
). Se isso puder ser generalizado (um único exemplo pode ser enganoso), sugiro esta alternativa:Se a maioria de suas consultas (importantes) incluir condições em ambas as colunas, um índice de várias colunas pode ser útil. Caso contrário, colunas únicas podem ser melhores no geral.
Disposição da tabela
Este é um palpite, já que não tenho informações completas.
Não use
oid
como nome de coluna. É fácil confundir com oOID
.Não use o nome
date
para uma coluna de carimbo de data/hora. Ou melhor: não use o nomedate
para nenhuma coluna, não use nomes de tipos-base para identificadores. Pode levar a erros confusos e mensagens de erro.Crie uma tabela de pesquisa para tipos e coloque apenas um pequeno número inteiro
type_id
na tabela grande. Embale bem os tipos de comprimento fixo para não desperdiçar espaço com preenchimento. Detalhes.Eu prefiro o tipo
text
(ouvarchar
sem limite de comprimento) sobrevarchar(n)
. Detalhes.Por exemplo:
Erwin, eu tenho alguns dos dados que você pediu. Desta vez, tentei uma consulta mais ambiciosa (espero que a maior parte do trabalho seja para um conjunto menor de pids e uma região geoespacial menor), mas algumas pessoas vão sobrecarregar o sistema e isso pode acabar não sendo a maior consulta no final.
Explique a análise do índice com (contorno, pid)
[O que é estranho aqui é que, embora a consulta especifique que a cláusula WHERE tem o esboço primeiro e o pid depois - e, portanto, deveria estar usando o índice Inventory_compound_idx, ela está usando o índice inverso icompound_idx]
E agora mudei a ordem da consulta para que o PID seja o primeiro na cláusula where, então ele deve usar o índice icompound_idx (pid, estrutura de tópicos).
(Você vê alguma vantagem real, eu não).