Estou testando o desempenho de inserção do Postgres. Eu tenho uma tabela com uma coluna com número como seu tipo de dados. Também existe um índice. Enchi o banco de dados usando esta consulta:
insert into aNumber (id) values (564),(43536),(34560) ...
Eu inseri 4 milhões de linhas muito rapidamente 10.000 por vez com a consulta acima. Depois que o banco de dados atingiu 6 milhões de linhas, o desempenho diminuiu drasticamente para 1 milhão de linhas a cada 15 minutos. Existe algum truque para aumentar o desempenho da inserção? Preciso de um ótimo desempenho de inserção neste projeto.
Usando o Windows 7 Pro em uma máquina com 5 GB de RAM.
sql
postgresql
bulkinsert
sql-insert
Luke101
fonte
fonte
Respostas:
Consulte preencher um banco de dados no manual do PostgreSQL, o excelente artigo de depesz sobre o tópico e esta questão de SO .
(Note-se que esta resposta é sobre dados em massa de carregamento em um DB existente ou criar um novo. Se você estiver interessado DB restaurar o desempenho com
pg_restore
oupsql
execução depg_dump
saída, grande parte desta não se aplica, uma vezpg_dump
epg_restore
já fazer coisas como criar aciona e indexa após concluir uma restauração de esquema + dados) .Há muito a ser feito. A solução ideal seria importar para uma
UNLOGGED
tabela sem índices, depois alterá-la para logado e adicionar os índices. Infelizmente, no PostgreSQL 9.4, não há suporte para alterar tabelas deUNLOGGED
para logadas. 9.5 adicionaALTER TABLE ... SET LOGGED
para permitir que você faça isso.Se você pode colocar seu banco de dados offline para a importação em massa, use
pg_bulkload
.De outra forma:
Desativar qualquer gatilho na mesa
Solte índices antes de iniciar a importação, recrie-os posteriormente. (Leva muito menos tempo para criar um índice em uma passagem do que para adicionar os mesmos dados progressivamente, e o índice resultante é muito mais compacto).
Ao fazer a importação em uma única transação, é seguro eliminar restrições de chave estrangeira, fazer a importação e recriar as restrições antes de confirmar. Não faça isso se a importação estiver dividida em várias transações, pois você poderá introduzir dados inválidos.
Se possível, use em
COPY
vez deINSERT
sSe você não pode usar,
COPY
considere o uso deINSERT
s com valores múltiplos, se possível. Você parece estar fazendo isso já. Não tente listar também muitos valores em um únicoVALUES
embora; esses valores precisam caber na memória algumas vezes; portanto, mantenha-o em algumas centenas por instrução.Lote suas inserções em transações explícitas, fazendo centenas de milhares ou milhões de inserções por transação. Não há limite prático para o AFAIK, mas o lote permite recuperar um erro marcando o início de cada lote nos dados de entrada. Novamente, você parece estar fazendo isso já.
Use
synchronous_commit=off
e muitocommit_delay
para reduzir os custos do fsync (). Isso não ajudará muito se você tiver agrupado seu trabalho em grandes transações, no entanto.INSERT
ouCOPY
em paralelo de várias conexões. Quantas depende do subsistema de disco do seu hardware; Como regra geral, você deseja uma conexão por disco rígido físico se estiver usando armazenamento conectado diretamente.Defina um
checkpoint_segments
valor alto e ativelog_checkpoints
. Veja os logs do PostgreSQL e verifique se ele não está reclamando sobre os pontos de verificação que ocorrem com muita frequência.Se, e somente se você não se importar em perder todo o cluster do PostgreSQL (seu banco de dados e outros no mesmo cluster), por danos catastróficos se o sistema travar durante a importação, você pode parar a página, definir
fsync=off
, iniciar a página, fazer a importação, então (vitalmente) pare Pg e ajustefsync=on
novamente. Consulte configuração WAL . Não faça isso se já houver dados importantes para você em qualquer banco de dados na sua instalação do PostgreSQL. Se você definir,fsync=off
também poderá definirfull_page_writes=off
; novamente, lembre-se de ligá-lo novamente após a importação para evitar corrupção de banco de dados e perda de dados. Consulte configurações não duráveis no manual da página.Você também deve ajustar o seu sistema:
Use SSDs de boa qualidade para armazenamento, tanto quanto possível. Bons SSDs com caches de write-back confiáveis e protegidos por energia tornam as taxas de confirmação incrivelmente mais rápidas. Eles são menos benéficos quando você segue os conselhos acima - o que reduz a liberação do disco / número de
fsync()
s - mas ainda pode ser uma grande ajuda. Não use SSDs baratos sem proteção adequada contra falta de energia, a menos que você não se preocupe em manter seus dados.Se você estiver usando RAID 5 ou RAID 6 para armazenamento conectado diretamente, pare agora. Faça backup dos seus dados, reestruture sua matriz RAID para RAID 10 e tente novamente. O RAID 5/6 é inútil para o desempenho de gravação em massa - embora um bom controlador RAID com um grande cache possa ajudar.
Se você tiver a opção de usar um controlador RAID de hardware com um grande cache de write-back com bateria, isso pode realmente melhorar o desempenho de gravação para cargas de trabalho com muitas confirmações. Isso não ajuda muito se você estiver usando a confirmação assíncrona com um commit_delay ou se estiver fazendo menos grandes transações durante o carregamento em massa.
Se possível, armazene WAL (
pg_xlog
) em um disco / matriz de discos separado. Não faz sentido usar um sistema de arquivos separado no mesmo disco. As pessoas geralmente escolhem usar um par RAID1 para o WAL. Novamente, isso tem mais efeito em sistemas com altas taxas de confirmação e tem pouco efeito se você estiver usando uma tabela não registrada como destino de carregamento de dados.Você também pode estar interessado em Otimizar o PostgreSQL para testes rápidos .
fonte
indisvalid
( postgresql.org/docs/8.3/static/catalog-pg-index.html ) como false, carregar os dados e colocar os índices on-line porREINDEX
?UNLOGGED
? Um teste rápido mostra algo como uma melhoria de 10 a 20%.O uso
COPY table TO ... WITH BINARY
que está de acordo com a documentação é " um pouco mais rápido que os formatos de texto e CSV ". Faça isso apenas se você tiver milhões de linhas para inserir e se estiver confortável com dados binários.Aqui está um exemplo de receita em Python, usando psycopg2 com entrada binária .
fonte
Além da excelente postagem de Craig Ringer e da depesz, se você deseja acelerar suas inserções através da interface ODBC ( psqlodbc ) usando inserções de instruções preparadas em uma transação, há algumas coisas extras que você precisa fazer para fazê-lo trabalhe rápido:
Protocol=-1
na cadeia de conexão. Por padrão, o psqlodbc usa o nível "Instrução", que cria um SAVEPOINT para cada instrução em vez de uma transação inteira, tornando as inserções mais lentas.UseServerSidePrepare=1
na cadeia de conexão. Sem essa opção, o cliente envia a instrução de inserção inteira junto com cada linha sendo inserida.SQLSetConnectAttr(conn, SQL_ATTR_AUTOCOMMIT, reinterpret_cast<SQLPOINTER>(SQL_AUTOCOMMIT_OFF), 0);
SQLEndTran(SQL_HANDLE_DBC, conn, SQL_COMMIT);
. Não há necessidade de abrir explicitamente uma transação.Infelizmente, o psqlodbc "implementa"
SQLBulkOperations
emitindo uma série de instruções de inserção não preparadas, para que, para obter a inserção mais rápida, seja necessário codificar manualmente as etapas acima.fonte
A8=30000000
na cadeia de conexão, também deve ser usado para acelerar as inserções.Passei cerca de 6 horas na mesma questão hoje. As inserções vão a uma velocidade 'normal' (menos de 3s por 100K) até 5MI (do total de 30MI) de linhas e, em seguida, o desempenho diminui drasticamente (até 1m por 100K).
Não vou listar todas as coisas que não funcionaram e cortar diretamente a carne.
I deixou cair uma chave primária na tabela de destino (que era um GUID) e meu 30mi ou linhas feliz fluiu ao seu destino a uma velocidade constante de menos de 3 segundos por 100K.
fonte
Se você inseriu colunas com UUIDs (o que não é exatamente o seu caso) e adiciona à resposta @Dennis (ainda não posso comentar), seja avisado do que usar gen_random_uuid () (requer o módulo PG 9.4 e pgcrypto). muito mais rápido que uuid_generate_v4 ()
vs
Além disso, é a maneira oficial sugerida de fazer isso
Isso reduziu o tempo de inserção de ~ 2 horas para ~ 10 minutos para 3,7M de linhas.
fonte
Para otimizar o desempenho da inserção, desative o índice, se for uma opção para você. Fora isso, um melhor hardware (disco, memória) também é útil
fonte
Também encontrei esse problema de desempenho de inserção. Minha solução é gerar algumas rotinas para concluir o trabalho de inserção. Enquanto isso,
SetMaxOpenConns
deve receber um número adequado, caso contrário, muitos erros de conexão aberta serão alertados.A velocidade de carregamento é muito mais rápida para o meu projeto. Esse trecho de código apenas deu uma idéia de como ele funciona. Os leitores devem poder modificá-lo facilmente.
fonte