Otimize o PostgreSQL para testes rápidos

203

Estou mudando para o PostgreSQL do SQLite para uma aplicação típica do Rails.

O problema é que a execução de especificações ficou lenta com o PG.
No SQLite, demorou ~ 34 segundos, no PG, é ~ 76 segundos, o que é mais do que 2x mais lento .

Então agora eu quero aplicar algumas técnicas para melhorar o desempenho das especificações com o SQLite sem modificações de código (idealmente, basta definir as opções de conexão, o que provavelmente não é possível).

Algumas coisas óbvias do alto da minha cabeça são:

  • Disco de RAM (seria bom ver uma boa configuração com o RSpec no OSX)
  • Tabelas não registradas (elas podem ser aplicadas em todo o banco de dados para que eu não tenha que alterar todos os scripts?)

Como você deve ter entendido, não me importo com a confiabilidade e o resto (o banco de dados é apenas uma coisa descartável aqui).
Preciso tirar o máximo proveito do PG e torná-lo o mais rápido possível .

A melhor resposta descreveria idealmente os truques para fazer exatamente isso, a configuração e as desvantagens desses truques.

UPDATE: fsync = off + full_page_writes = offdiminuiu o tempo para ~ 65 segundos (~ -16 segundos). Bom começo, mas longe do objetivo de 34.

UPDATE 2: Eu tentei disco uso RAM , mas o ganho de desempenho estava dentro de uma margem de erro. Portanto, não parece valer a pena.

ATUALIZAÇÃO 3: * Encontrei o maior gargalo e agora minhas especificações são mais rápidas que as do SQLite.

O problema foi a limpeza do banco de dados que fez o truncamento . Aparentemente, o SQLite é muito rápido lá.

Para "corrigi-lo", abro uma transação antes de cada teste e a reverto no final.

Alguns números para ~ 700 testes.

  • Truncamento: SQLite - 34s, PG - 76s.
  • Transação: SQLite - 17s, PG - 18s.

Aumento de velocidade de 2x para SQLite. Aumento de velocidade de 4x para PG.

Dmytrii Nagirniak
fonte
2
Eu realmente duvido que você faça isso tão rápido quanto o SQLite. O SQLite com um único usuário é incrivelmente rápido. O design do SQLite é muito rápido, com baixa contagem de usuários e escala insuficiente; O design da Pg dimensiona bem, mas não é tão rápido para trabalhos em massa simples com apenas um usuário.
Craig Ringer
1
Sei disso, mas há um caso específico em que espero otimizar o PG para (execuções de teste), para que seja o mais rápido possível. Eu não me importo que seja um pouco mais lento lá, mas 2,2x é um pouco lento demais. Entendeu o que eu quis dizer?
Dmytrii Nagirniak
+1 Eu ficaria muito interessado em atualizações sobre a abordagem de disco RAM, se você tiver algum resultado sobre isso.
tscho 23/02/12
@tscho eu definitivamente vou postar aqui. Mas preciso de um tempo, já que estou trabalhando em outras coisas e "pesquisando" as coisas do PG em "segundo plano".
Dmytrii Nagirniak
está inserindo os dados seu problema ou está consultando ? Não está claro em sua pergunta.
a_horse_with_no_name 23/02

Respostas:

281

Primeiro, sempre use a versão mais recente do PostgreSQL. As melhorias de desempenho estão sempre chegando; portanto, você provavelmente está perdendo tempo se estiver ajustando uma versão antiga. Por exemplo, o PostgreSQL 9.2 melhora significativamente a velocidadeTRUNCATE e, naturalmente, adiciona varreduras somente de índice. Mesmo versões menores sempre devem ser seguidas; consulte a política de versão .

Não é

Você não colocar um espaço de tabela em um disco RAM ou outro armazenamento não-duráveis .

Se você perder um espaço de tabela, todo o banco de dados poderá ser danificado e difícil de usar sem um trabalho significativo. Há muito pouca vantagem nisso se comparado ao uso de UNLOGGEDtabelas e à disponibilidade de muita RAM para cache.

Se você realmente deseja um sistema baseado em ramdisk, initdbum cluster totalmente novo no ramdisk, initdbinserindo uma nova instância do PostgreSQL no ramdisk, para ter uma instância do PostgreSQL completamente descartável.

Configuração do servidor PostgreSQL

Ao testar, você pode configurar seu servidor para uma operação não durável, mas mais rápida .

Este é um dos únicos usos aceitáveis ​​para a fsync=offconfiguração no PostgreSQL. Essa configuração praticamente diz ao PostgreSQL para não se preocupar com gravações ordenadas ou qualquer outra coisa desagradável de proteção à integridade de dados e segurança contra falhas, dando permissão para descartar totalmente seus dados se você perder energia ou ocorrer uma falha no sistema operacional.

Desnecessário dizer que você nunca deve ativar a fsync=offprodução, a menos que esteja usando a Pg como um banco de dados temporário para dados que você pode gerar novamente de outro lugar. Se e somente se você estiver fazendo para desativar o fsync, também é possível full_page_writesdesativar, pois não serve mais para nada. Cuidado fsync=offe full_page_writesaplique no nível do cluster , para que eles afetem todos os bancos de dados na sua instância do PostgreSQL.

Para uso em produção, é possível usar synchronous_commit=offe definir um commit_delay, pois você terá muitos dos mesmos benefícios que fsync=offsem o risco gigante de corrupção de dados. Você tem uma pequena janela de perda de dados recentes se ativar a confirmação assíncrona - mas é isso.

Se você tiver a opção de alterar levemente o DDL, também poderá usar as UNLOGGEDtabelas na página 9.1+ para evitar completamente o log do WAL e obter um aumento real da velocidade com o custo de as tabelas serem apagadas se o servidor travar. Não há opção de configuração para tornar todas as tabelas desmarcadas; ela deve ser definida durante CREATE TABLE. Além de ser bom para testar, isso é útil se você tiver tabelas cheias de dados gerados ou sem importância em um banco de dados que, de outra forma, contém coisas que você precisa estar seguro.

Verifique seus logs e veja se você está recebendo avisos sobre muitos pontos de verificação. Se você é, você deve aumentar seus pontos de verificação . Você também pode ajustar seu checkpoint_completion_target para facilitar as gravações.

Ajuste shared_bufferspara ajustar sua carga de trabalho. Isso depende do sistema operacional, depende do que mais está acontecendo com sua máquina e requer algumas tentativas e erros. Os padrões são extremamente conservadores. Pode ser necessário aumentar o limite máximo de memória compartilhada do sistema operacional se você aumentar shared_buffersno PostgreSQL 9.2 e abaixo; 9.3 e acima mudaram a maneira como eles usam a memória compartilhada para evitar isso.

Se você estiver usando apenas algumas conexões que dão muito trabalho, aumente work_mempara fornecer mais memória RAM para brincar, etc. Lembre-se de que uma work_memconfiguração muito alta pode causar problemas de falta de memória porque ela não é classificada por categoria. por conexão para que uma consulta possa ter várias classificações aninhadas. Você realmente precisa aumentar work_memse conseguir ver as classificações sendo derramadas no disco EXPLAINou logadas com a log_temp_filesconfiguração (recomendável), mas um valor mais alto também pode permitir que a Pg escolha planos mais inteligentes.

Como dito por outro pôster aqui, é aconselhável colocar o xlog e as tabelas / índices principais em HDDs separados, se possível. Partições separadas são inúteis, você realmente deseja unidades separadas. Essa separação tem muito menos benefícios se você estiver executando com fsync=offe quase nenhum se estiver usando UNLOGGEDtabelas.

Por fim, ajuste suas consultas. Verifique random_page_coste seq_page_costreflita o desempenho do seu sistema, verifique se effective_cache_sizeestá correto, etc. Use EXPLAIN (BUFFERS, ANALYZE)para examinar planos de consulta individuais e ligue o auto_explainmódulo para relatar todas as consultas lentas. Muitas vezes, é possível melhorar drasticamente o desempenho da consulta criando um índice apropriado ou aprimorando os parâmetros de custo.

AFAIK não há como definir um banco de dados ou cluster inteiro como UNLOGGED. Seria interessante poder fazer isso. Considere perguntar na lista de discussão do PostgreSQL.

Ajuste do SO do host

Também é possível fazer alguns ajustes no nível do sistema operacional. A principal coisa que você pode querer fazer é convencer o sistema operacional a não liberar gravações no disco de forma agressiva, pois você realmente não se importa quando / se elas o fazem no disco.

No Linux, você pode controlar isso com o subsistema de memória virtual 's dirty_*configurações, como dirty_writeback_centisecs.

O único problema com o ajuste das configurações de write-back é muito frouxo é que uma descarga por outro programa pode causar a descarga de todos os buffers acumulados do PostgreSQL também, causando grandes paradas enquanto tudo bloqueia as gravações. Você pode aliviar isso executando o PostgreSQL em um sistema de arquivos diferente, mas algumas descargas podem ser no nível do dispositivo ou no host inteiro e não no sistema de arquivos, portanto você não pode confiar nisso.

Esse ajuste realmente exige que você faça as configurações para ver o que funciona melhor para sua carga de trabalho.

Nos kernels mais recentes, convém garantir que vm.zone_reclaim_modeseja definido como zero, pois isso pode causar problemas graves de desempenho nos sistemas NUMA (na maioria dos sistemas atualmente) devido a interações com o gerenciamento do PostgreSQL shared_buffers.

Ajuste de consulta e carga de trabalho

São coisas que exigem alterações de código; eles podem não combina com você. Algumas são coisas que você pode aplicar.

Se você não estiver trabalhando em lotes em transações maiores, inicie. Muitas transações pequenas são caras, portanto, você deve agrupar as coisas sempre que possível e prático. Se você estiver usando a confirmação assíncrona, isso é menos importante, mas ainda é altamente recomendado.

Sempre que possível, use tabelas temporárias. Eles não geram tráfego WAL, portanto, são muito mais rápidos para inserções e atualizações. Às vezes, vale a pena incluir um monte de dados em uma tabela temporária, manipulá-los da maneira que você precisar e, em seguida, fazer uma INSERT INTO ... SELECT ...cópia para a tabela final. Observe que as tabelas temporárias são por sessão; se a sua sessão terminar ou você perder a conexão, a tabela temporária desaparecerá e nenhuma outra conexão poderá ver o conteúdo das tabelas temporárias de uma sessão.

Se você estiver usando o PostgreSQL 9.1 ou mais recente, poderá usar UNLOGGEDtabelas para dados que pode perder, como o estado da sessão. Eles são visíveis em diferentes sessões e preservados entre as conexões. Eles são truncados se o servidor for desligado de maneira não limpa, para que não possam ser usados ​​para qualquer coisa que você não possa recriar, mas são ótimos para caches, visualizações materializadas, tabelas de estado etc.

Em geral, não DELETE FROM blah;. Use em TRUNCATE TABLE blah;vez disso; é muito mais rápido quando você descarta todas as linhas em uma tabela. Trunque muitas tabelas em uma TRUNCATEchamada, se puder. Há uma ressalva se você estiver fazendo muitas TRUNCATESmesas pequenas repetidamente; veja: Velocidade de truncamento do Postgresql

Se você não tiver índices em chaves estrangeiras, os DELETEs que envolvem as chaves primárias referenciadas por essas chaves estrangeiras serão terrivelmente lentos. Crie esses índices, se você espera DELETEda tabela (s) referenciada (s). Os índices não são necessários para TRUNCATE.

Não crie índices desnecessários. Cada índice tem um custo de manutenção. Tente usar um conjunto mínimo de índices e permita que as varreduras de índice de bitmap as combinem, em vez de manter muitos índices enormes e caros de várias colunas. Onde os índices são necessários, tente preencher a tabela primeiro e depois crie índices no final.

Hardware

Ter RAM suficiente para armazenar todo o banco de dados é uma grande vitória, se você puder gerenciá-lo.

Se você não tiver RAM suficiente, quanto mais rápido o armazenamento, melhor. Mesmo um SSD barato faz uma enorme diferença em relação à ferrugem. Porém, não confie em SSDs baratos para produção, eles geralmente não são seguros contra falhas e podem consumir seus dados.

Aprendendo

O livro de Greg Smith, PostgreSQL 9.0 High Performance, continua relevante, apesar de se referir a uma versão um pouco mais antiga. Deve ser uma referência útil.

Entre na lista de discussão geral do PostgreSQL e siga-a.

Lendo:

Craig Ringer
fonte
10
Também posso recomendar o PostgreSQL 9.0 High Performance da @GregSmith, é realmente uma ótima leitura. O livro aborda todos os aspectos do ajuste de desempenho, desde o layout do disco até o ajuste de consultas, e fornece uma compreensão muito boa dos internos do PG.
tscho 23/02/12
10
Não lancei uma atualização do livro para o PostgreSQL 9.1, a única versão desde a sua publicação, porque não havia mudanças relacionadas ao desempenho suficientes na 9.1 para justificá-lo.
Greg Smith
3
Ótima redação. Assim como uma pequena atualização, “Você pode precisar aumentar o limite máximo de memória compartilhada do sistema operacional se aumentar o shared_buffers” não é mais verdadeiro (para a maioria dos usuários) no PostgreSQL 9.3: postgresql.org/docs/9.3/static/release-9- #Html # AEN114343
Gunnlaugur Briem
1
@brauliobo Meus testes costumam fazer muitos TXs com TPS alto ... porque tento simular a produção, incluindo cargas de trabalho pesadas por simultaneidade. Se você quer dizer "conexão única, teste linear", eu concordo com você.
Craig Ringer
1
stackoverflow.com/questions/11419536/… DELETE pode ser mais rápido que TRUNCATE para tabelas com poucas linhas, o que provavelmente acontece nos testes.
Jonathan Crosmer
9

Use um layout de disco diferente:

  • disco diferente por $ PGDATA
  • disco diferente para $ PGDATA / pg_xlog
  • disco diferente para arquivos tem (por banco de dados $ PGDATA / base // pgsql_tmp) (consulte a observação sobre work_mem)

ajustes do postgresql.conf:

  • shared_memory: 30% da RAM disponível, mas não mais que 6 a 8 GB. Parece ser melhor ter menos memória compartilhada (2 GB - 4 GB) para cargas de trabalho intensivas de gravação
  • work_mem: principalmente para consultas selecionadas com classificações / agregações. Isso é por configuração de conexão e a consulta pode alocar esse valor várias vezes. Se os dados não couberem, o disco será usado (pgsql_tmp). Marque "explique analisar" para ver quanta memória você precisa
  • fsync e synchronous_commit: os valores padrão são seguros, mas se você puder tolerar a perda de dados, poderá desativá-lo.
  • random_page_cost: se você tiver SSD ou matriz RAID rápida, poderá reduzi-la para 2,0 (RAID) ou ainda mais (1,1) para SSD
  • checkpoint_segments: você pode subir 32 ou 64 e alterar checkpoint_completion_target para 0,9. Um valor mais baixo permite uma recuperação mais rápida após a falha
mys
fonte
4
Observe que, se você já está executando fsync=off, colocar o pg_xlog em um disco separado não melhora muito.
intgr 23/02/12
O valor de 1,1 para SSD parece muito desqualificado. Reconheço que é o que alguns profissionais recomendam cegamente. Até os SSDs são significativamente mais rápidos para leituras sequenciais do que leituras aleatórias.
Acumenos
@ABB Sim, mas você também tem efeitos de cache de buffer do sistema operacional em funcionamento. Todos esses parâmetros são uma mão-wavey pouco de qualquer maneira ...
Craig Ringer