Eu tenho uma tabela do Postgres com ~ 2.1 milhões de linhas. Eu executei a atualização abaixo:
WITH stops AS (
SELECT id,
rank() OVER (ORDER BY offense_timestamp,
defendant_dl,
offense_street_number,
offense_street_name) AS stop
FROM consistent.master
WHERE citing_jurisdiction=1
)
UPDATE consistent.master
SET arrest_id=stops.stop
FROM stops
WHERE master.id = stops.id;
Essa consulta levou 39 horas para ser executada. Estou executando isso em um processador de laptop i7 Q720 de 4 núcleos (físico), muita RAM, nada mais executando a grande maioria das vezes. Sem restrições de espaço no disco rígido. A mesa havia sido recentemente aspirada, analisada e reindexada.
Durante todo o tempo em que a consulta estava em execução, pelo menos após a WITH
conclusão inicial , o uso da CPU era geralmente baixo e o HDD estava em uso 100%. O disco rígido estava sendo usado com tanta força que qualquer outro aplicativo rodava consideravelmente mais devagar que o normal.
A configuração de energia do laptop estava em alto desempenho (Windows 7 x64).
Aqui está o EXPLAIN:
Update on master (cost=822243.22..1021456.89 rows=2060910 width=312)
CTE stops
-> WindowAgg (cost=529826.95..581349.70 rows=2060910 width=33)
-> Sort (cost=529826.95..534979.23 rows=2060910 width=33)
Sort Key: consistent.master.offense_timestamp, consistent.master.defendant_dl, consistent.master.offense_street_number, consistent.master.offense_street_name
-> Seq Scan on master (cost=0.00..144630.06 rows=2060910 width=33)
Filter: (citing_jurisdiction = 1)
-> Hash Join (cost=240893.51..440107.19 rows=2060910 width=312)
Hash Cond: (stops.id = consistent.master.id)
-> CTE Scan on stops (cost=0.00..41218.20 rows=2060910 width=48)
-> Hash (cost=139413.45..139413.45 rows=2086645 width=268)
-> Seq Scan on master (cost=0.00..139413.45 rows=2086645 width=268)
citing_jurisdiction=1
exclui apenas algumas dezenas de milhares de linhas. Mesmo com essa WHERE
cláusula, ainda estou operando em mais de 2 milhões de linhas.
O disco rígido é criptografado com o TrueCrypt 7.1a. Que atrasa as coisas um pouco, mas não o suficiente para causar uma consulta para tomar que muitas horas.
A WITH
peça leva apenas cerca de 3 minutos para ser executada.
O arrest_id
campo não tinha índice para chave estrangeira. Existem 8 índices e 2 chaves estrangeiras nesta tabela. Todos os outros campos da consulta são indexados.
O arrest_id
campo não tinha restrições, exceto NOT NULL
.
A tabela possui 32 colunas no total.
arrest_id
é do tipo variando de caracteres (20) . Percebo que rank()
produz um valor numérico, mas tenho que usar caracteres variáveis (20) porque tenho outras linhas em citing_jurisdiction<>1
que esses dados não são numéricos para esse campo.
O arrest_id
campo estava em branco para todas as linhas com citing_jurisdiction=1
.
Este é um laptop pessoal sofisticado (de 1 ano atrás). Eu sou o único usuário. Nenhuma outra consulta ou operação estava em execução. O bloqueio parece improvável.
Não há gatilhos em nenhum lugar nesta tabela ou em qualquer outro lugar no banco de dados.
Outras operações nesse banco de dados nunca levam uma quantidade abundante de tempo. Com a indexação adequada, as SELECT
consultas geralmente são bastante rápidas.
fonte
Seq Scan
são um pouco assustador ...Respostas:
Aconteceu recentemente algo semelhante com uma tabela de 3,5 milhões de linhas. Minha atualização nunca terminaria. Depois de muitas experiências e frustrações, finalmente encontrei o culpado. Acabou sendo os índices na tabela que estão sendo atualizados.
A solução foi eliminar todos os índices da tabela que está sendo atualizada antes de executar a instrução update. Depois disso, a atualização terminou em alguns minutos. Depois que a atualização foi concluída, recriei os índices e voltei aos negócios. Provavelmente, isso não o ajudará neste momento, mas pode alguém procurar respostas.
Eu manteria os índices na tabela da qual você está puxando os dados. Esse não precisará continuar atualizando nenhum índice e deve ajudar a encontrar os dados que você deseja atualizar. Funcionou bem em um laptop lento.
fonte
O seu maior problema é fazer grandes quantidades de trabalho com muita gravação e muita procura em um disco rígido de laptop. Isso nunca será rápido, não importa o que você faça, especialmente se for o tipo de unidade mais lenta de 5400 RPM enviada em muitos laptops.
TrueCrypt torna as coisas mais lentas do que "um pouco" para gravações. As leituras serão razoavelmente rápidas, mas as gravações fazem o RAID 5 parecer mais rápido. A execução de um banco de dados em um volume TrueCrypt será uma tortura para gravações, especialmente gravações aleatórias.
Nesse caso, acho que você estaria perdendo seu tempo tentando otimizar a consulta. Você está reescrevendo a maioria das linhas de qualquer maneira, e será lento com sua situação de gravação horrível. O que eu recomendaria é:
Eu suspeito que será mais rápido do que apenas remover e recriar as restrições sozinho, porque um UPDATE terá padrões de gravação bastante aleatórios que matarão seu armazenamento. Duas inserções em massa, uma em uma tabela não registrada e outra em uma tabela registrada no WAL sem restrições, provavelmente serão mais rápidas.
Se você possui backups absolutamente atualizados e não se importa de restaurar seu banco de dados a partir de backups, também pode reiniciar o PostgreSQL com o
fsync=off
parâmetro efull_page_writes=off
temporariamente para esta operação em massa. Qualquer problema inesperado, como perda de energia ou falha no sistema operacional, deixará seu banco de dados irrecuperávelfsync=off
.O POSTGreSQL equivalente a "sem registro" é usar tabelas não registradas. Essas tabelas não registradas são truncadas se o banco de dados for encerrado de maneira suja enquanto estiver sujo. O uso de tabelas não registradas reduzirá pela metade a carga de gravação e reduzirá o número de pesquisas, para que elas sejam MUITO mais rápidas.
Como no Oracle, pode ser uma boa idéia descartar um índice e depois recriá-lo após uma grande atualização em lote. O planejador do PostgreSQL não pode descobrir que uma grande atualização está ocorrendo, pausar as atualizações do índice e reconstruir o índice no final; mesmo que pudesse, seria muito difícil descobrir em que ponto isso valia a pena, especialmente com antecedência.
fonte
HOT
, é melhor deixar os índices no lugar. Caso contrário, é provável que você queira largar e recriar. A coluna não está indexada, mas para poder fazer uma atualização HOT também é preciso haver espaço livre na mesma página, portanto, depende um pouco da quantidade de espaço morto existente na tabela. Se for principalmente de gravação, eu diria que abandone todos os índices. Se houver lotes atualizados, pode haver buracos e você pode ficar bem. Ferramentas comopageinspect
epg_freespacemap
podem ajudar a determinar isso.Alguém dará uma resposta melhor para o Postgres, mas aqui estão algumas observações da perspectiva do Oracle que podem ser aplicadas (e os comentários são muito longos para o campo de comentário).
Minha primeira preocupação seria tentar atualizar 2 milhões de linhas em uma transação. No Oracle, você escreveria uma imagem anterior de cada bloco sendo atualizado para que outra sessão ainda tenha uma leitura consistente sem ler seus blocos modificados e você poderá reverter. Essa é uma longa reversão sendo construída. Geralmente, é melhor você fazer as transações em pequenos pedaços. Diga 1.000 registros por vez.
Se você tiver índices na tabela e a tabela for considerada fora de operação durante a manutenção, é melhor remover os índices antes de uma grande operação e recriá-los novamente depois. Mais barato, tentando constantemente manter os índices a cada registro atualizado.
O Oracle permite dicas "sem registro" nas instruções para interromper o diário. Ele acelera bastante as instruções, mas deixa seu banco de dados em uma situação "irrecuperável". Então, você deseja fazer backup antes e fazer backup novamente imediatamente depois. Não sei se o Postgres tem opções semelhantes.
fonte
VACUUM
é apenas metade da resposta; O PostgreSQL também usa o chamado "write ahead log" (efetivamente um diário) para fornecer confirmações atômicas e proteger contra gravações parciais, etc. Veja postgresql.org/docs/current/static/wal-intro.html