Eu tenho uma tabela que conterá alguns dados pré-calculados com base em outras tabelas. (Computar em tempo real é muito caro computacionalmente, dado o tamanho dos dados com os quais tenho que lidar.) Estarei gerando de forma incremental à medida que os dados de origem forem adicionados. (Nunca precisarei UPDATE
disso em uso normal; partes podem ser excluídas e regeneradas.) A tabela será bastante grande. Atualmente, são cerca de 50 milhões de linhas e crescerão a cada ano.
A maioria das consultas nesta tabela será filtrada por uma coluna de ID de chave estrangeira. Dessa forma, eles têm um desempenho melhor se todas as linhas desse ID forem agrupadas nas mesmas páginas. Posso garantir essa ordenação no disco criando um índice e chamando CLUSTER
periodicamente, mas isso obviamente não é o ideal, pois exigiria algum tipo de tarefa agendada, coordenando com o uso e outras tarefas agendadas, etc.
No entanto, como estou gerando esses dados em pedaços relacionados à chave estrangeira que quero CLUSTER
, posso facilmente colocar uma ORDER BY
cláusula no INSERT
comando:
INSERT INTO big_table (source_table1_id,a,b,c)
SELECT
source_table1_id,
5 /* some formula */,
/* ... */
FROM source_table1
JOIN source_table2 ON ...
...
WHERE ... /* some condition indicating what needs to be generated */
ORDER BY source_table1_id
Isso afetará a ordem de armazenamento em disco, agrupando as linhas em um número próximo ao mínimo de páginas? E se isso acontecer, existem outros processos que podem atrapalhar a ordem do disco mais tarde?
Atualmente estou usando o PostgreSQL 9.3, mas gostaria de saber sobre as versões mais recentes também para atualizações.
As linhas serão processadas na ordem garantida, mas isso não significa que após serem inseridas elas ficarão localizadas uma ao lado da outra. Isso só será possível se os registros nunca forem excluídos ou atualizados em sua tabela. Depois de atualizar ou excluir algumas linhas, após a limpeza, você provavelmente terá espaço livre no meio da tabela, para onde irão os próximos registros inseridos.
Alguns detalhes extras nesta questão .
O Postgres escreve fisicamente as tuplas sequencialmente como INSERTed. Se você fizer isso em uma nova tabela ou em uma tabela sem tuplas mortas, obterá exatamente o mesmo resultado que obteria
CLUSTER
em um índice com a mesma ordem de classificação que seu arquivoINSERT
. O efeito deCLUSTER
se deteriora com gravações posteriores na tabela da mesma maneira (e permanece intacto enquanto você nuncaDELETE
ouUPDATE
- ouINSERT
quebra a ordem desejada).Algumas respostas se concentram nesses efeitos de gravações posteriores e perdem o ponto da pergunta. A resposta para sua pergunta é basicamente:
SIM , inserir em ordem tem o mesmo efeito que agrupar.
com base em algumas condições:
Isso significa que você acessa a linha com o mesmo ID de uma só vez, não um intervalo de IDs sequenciais. Então tudo que você precisa é de tuplas agrupadas por ID, a ordem física entre os IDs não tem sentido e é irrelevante.
E:
Ou seja, "pedaços" incluem todas as linhas para o mesmo ID em sequência. Não há outras linhas para o mesmo ID inserido mais cedo ou mais tarde. Então algo como:
E:
A parte sobre
DELETE
é a única parte levemente problemática. Se você nunca excluísse também, estaria feito aqui. Se por "porções" você quer dizer todas as linhas com disseID
de uma vez, você ainda é bom, principalmente . Ao excluir e inserir na mesma transação, não há fragmentação nos IDs. (As tuplas excluídas ainda não estão "mortas" e não são substituídas na mesma transação.)Tuplas mortas começam a inchar a tabela e inserções posteriores podem começar a preencher buracos físicos, que é onde a fragmentação pode começar. O inchaço com tuplas mortas tem vários efeitos ruins acumulados, mas o acesso ao índice em todas as linhas para um determinado ID não é afetado.
Mas tudo isso é ortogonal à sua pergunta, pois as mesmas considerações se aplicam a
CLUSTER
.Você já considerou pg_repack , que pode fazer o mesmo que
CLUSTER
, só que sem bloqueio exclusivo na tabela. Eles acabaram de adicionar o Postgres 9.6 à lista de versões suportadas esta semana.Relacionado:
Não, não.
ORDER BY
, o pedido de inserção pode ser totalmente aleatórioORDER BY
anINSERT
ainda pode deixar lacunas porque não reescreve a tabela.Considere isto..
Por implementação, isso insere as linhas na ordem em que foram geradas. Não é garantido, mas atualmente tem. O SQL não garante nada da ordem de retorno sem um
ORDER BY
. No entanto, os dados não professam nenhuma ordem útil quando gerados dessa forma, portanto, isso é meramente trivial e não significativo para o desempenho da consulta. Agora podemos fazer isso..Agora as linhas
foo
estão ordenadas porbar
, isso pode tornar certas operações mais rápidas que usamfoo_bar_idx
.O que acontece se essas linhas já estiverem nessa ordem. O que acontece se o índice se alinhar com a linha e o clustering não reordenar nada? Então nada acontece. Mas esse não é um caso de uso típico, mesmo sem
INSERT
eDELETE
. No PostGIS, inserimos dados o tempo todo e agrupamos tabelas complexas de geometrias por sua caixa delimitadora . As comparações de caixa delimitadora são abstratas, mas tornam as coisas que as usam substancialmente mais rápidas.Se a tabela nunca for atualizada ou excluída, as linhas inseridas serão ordenadas fisicamente em sua ordem de inserção cronológica. Mas se ele for excluído ou atualizado, a limpeza da tabela criará buracos de espaço livre na tabela, e as linhas recém-inseridas podem ser espalhadas onde quer que se encaixem nesses buracos. Isso seria um problema menor se as exclusões ocorrerem em grandes conjuntos de dados especificados por um intervalo na mesma coluna pela qual você deseja classificar. Nesse caso, páginas inteiras de dados serão excluídas juntas, liberando esse espaço para serem reutilizados em conjunto.
É improvável que seu INSERT INTO...SELECT...ORDER BY seja eficaz, porque a ordenação ocorrerá apenas em partes. A menos que seus pedaços sejam muito grandes, ou os próprios pedaços sejam processados em ordem, bem como ordenados dentro de cada pedaço, é improvável que a ordenação de pedaços seja muito boa.
Você pode olhar para o intervalo de particionamento de sua tabela na chave de classificação. Isso pode resolver o problema apenas mantendo valores semelhantes juntos. Caso contrário, pelo menos faria com que o CLUSTER de cada partição separada levasse muito menos tempo do que o CLUSTER de uma tabela gigante, o que pode tornar mais fácil agendá-los.