PostgreSQL: sem mais VACUUM, sem mais bloat
(orioledata.com)- O OrioleDB, um novo mecanismo de armazenamento para PostgreSQL, elimina o processo de VACUUM, que consome muitos recursos, e resolve o problema de bloat em tabelas
- O PostgreSQL tradicional exige vacuum manual e autovacuum devido à sua arquitetura MVCC, e ambos os métodos consomem recursos significativos do sistema
- O OrioleDB processa alterações de dados com eficiência e consolida dados fragmentados por meio de undo logs em nível de linha e de bloco e mesclagem automática de páginas
- Em um benchmark sintético, ao atualizar apenas um único índice, mesclar automaticamente páginas esparsas e aplicar WAL em nível de linha, alcançou TPS 5 vezes maior e economia de recursos
- Ao resolver o antigo desafio do VACUUM, reduz a carga de manutenção e oferece melhorias de desempenho e experiência do usuário no PostgreSQL
História e limitações do VACUUM no PostgreSQL
- O processo de VACUUM é um artefato histórico originado do conceito de infinite time-travel do projeto Berkley Postgres, conceito que depois foi abandonado pela comunidade
- No entanto, isso levou à implementação de um sistema MVCC (Multi-Version Concurrency Control) vulnerável a bloat em tabelas
- O MVCC é vantajoso para o processamento de transações concorrentes, mas introduziu a necessidade de vacuuming manual para limpar dados antigos e desnecessários
- O vacuuming manual é uma tarefa intensiva em trabalho e uma possível fonte de ineficiência do sistema
- Depois, foi introduzido o autovacuum automatizado para reduzir a carga de trabalho manual
- Foi um grande avanço, mas não uma solução perfeita; mesmo sendo automático, ainda consome recursos significativos do sistema
- Isso é citado como um dos motivos pelos quais a Uber migrou do PostgreSQL para o MySQL e como uma das 10 reclamações de Richard Branson sobre o PostgreSQL
- A introdução de atualizações Heap-Only Tuples (HOT) e do microvacuum reduziu a necessidade de vacuum em tabelas inteiras
- Ainda assim, o VACUUM continuou sendo uma operação intensiva em recursos, e as tabelas permaneceram vulneráveis a bloat
- Isso foi mencionado como a parte do PostgreSQL mais odiada pela equipe da OtterTune
- Apesar dessas limitações, muitas organizações e desenvolvedores continuam usando PostgreSQL por sua robustez, extensibilidade e comunidade forte
- A OtterTune reconheceu o problema, mas decidiu continuar usando PostgreSQL
Principais recursos do OrioleDB
- O OrioleDB é um novo mecanismo PostgreSQL desenvolvido com o objetivo principal de proteger tabelas contra bloat e eliminar a necessidade de manutenção periódica como o VACUUM
- Ele faz isso com undo logs em nível de linha e de bloco e mesclagem automática de páginas
- Undo logs em nível de linha e de bloco oferecem controle mais granular, permitindo processar alterações de dados com eficiência
- O recurso de mesclagem automática de páginas consolida continuamente dados fragmentados em segundo plano
- Como cada técnica funciona
- O undo log em nível de linha permite atualizações in-place
- O undo log em nível de bloco remove do armazenamento principal tuplas que foram excluídas, mas ainda são visíveis para algumas transações, liberando espaço para novas tuplas
- A mesclagem automática de páginas esparsas protege tabelas e índices contra bloat mesmo após muitas exclusões
- Como resultado, há menos intervenção manual, menor consumo de recursos e menor vulnerabilidade das tabelas a bloat
Benchmark
- O benchmark sintético foi configurado com um script de inicialização que cria 1 tabela e 5 índices
- Criação da tabela
testcom chave primáriaide colunasvalue1~value4(float8),ts(timestamp) - Criação de índices em
value1,value2,value3,value4ets, respectivamente
- Criação da tabela
- O script pgbench usa um formato de upsert que faz uma atualização esparsa de um índice em caso de conflito
- Executa INSERT com um
idaleatório no intervalo de 1 a 10.000.000; em caso de conflito, atualiza apenasts - Essa atualização esparsa causa bloat de índice em tabelas PostgreSQL heap comuns
- Executa INSERT com um
- Vantagens do design do OrioleDB mostradas por esse benchmark
- Graças ao undo log e às atualizações in-place, o OrioleDB atualiza apenas o único índice cujo valor mudou, enquanto o mecanismo heap do PostgreSQL, ao atualizar um único campo indexado, invalida o HOT e faz todos os índices serem atualizados
- A mesclagem automática de páginas protege índices esparsos contra bloat, e páginas esparsas são mescladas automaticamente
- O WAL em nível de linha ocupa muito menos espaço que o WAL em nível de bloco, gerando redução de IOPS ao registrar WAL
- Números entregues pelo OrioleDB como resultado das melhorias acumuladas
- TPS 5 vezes maior por transação
- Carga de CPU 2,3 vezes menor por transação
- IOPS 22 vezes menor por transação
- Sem bloat em tabelas e índices
Importância do OrioleDB
- Com a introdução do OrioleDB, a comunidade PostgreSQL entra em uma nova era em que o VACUUM se torna coisa do passado
- Ele apresenta uma solução para um dos problemas mais antigos do PostgreSQL, oferecendo aos usuários maior eficiência e menor carga de manutenção
1 comentários
Opiniões no Hacker News
Ainda assim, algumas coisas me incomodam. O processamento em nível de linha cria problemas de alinhamento de escrita e de alinhamento de fsync, e páginas são mais fáceis de alinhar do que linhas de tamanho arbitrário
O PostgreSQL é muito, talvez extremamente, conservador quanto à segurança dos dados, e isso é alcançado principalmente com fsync nos momentos adequados; o impacto disso se espalha por toda a pilha de I/O, incluindo o firmware dos SSDs, deixando tudo mais lento
MVCC é muito bom para acesso concorrente, mas a documentação do Oriole não diz sob quais condições de concorrência o gráfico foi produzido
O título e a introdução do documento do Oriole se concentram em resolver o VACUUM, mas não parecem demonstrar que o gráfico de “onda quadrada” visto no PostgreSQL seja realmente causado, em sua maior parte, pelo VACUUM. Outro benchmark da Percona (https://www.percona.com/blog/evaluating-checkpointing-in-postgresql/) não mostra esse padrão claro de onda quadrada
Os autores provavelmente estão cientes desses problemas, então poderiam escrever um resumo de como os abordaram
https://github.com/orioledb/orioledb/blob/main/doc/arch.md
Como o PostgreSQL é conservador quanto à segurança dos dados, o primeiro objetivo é se tornar uma extensão pura. Para virar parte do PostgreSQL, será preciso passar pelo teste do tempo
É verdade que faltavam informações de concorrência, e adicionamos ao post do blog o tipo de VM e as informações de concorrência
O padrão de onda quadrada é, de fato, causado pelos checkpoints. Aqui, a causa da melhoria na verdade não é o VACUUM, mas a forma de modificar apenas os índices relacionados, além do WAL em nível de linha, que reduz o I/O total
É legal, mas fico curioso para saber como vocês planejam acompanhar o ciclo de releases do projeto-mãe
Se vocês fizeram um fork de um grande projeto open source e aplicaram mudanças consideráveis, e não acham que essas mudanças serão aceitas upstream, não sei se isso é uma boa ideia
É preciso deixar claro se é um brinquedo feito por diversão ou um projeto sério que será mantido daqui para frente
Se for um projeto sério, é preciso explicitar o plano para que ele não vire software abandonado no futuro, o plano para absorver releases futuros do projeto original, ou então o plano de se separar como um projeto completamente distinto
Por ser uma extensão, parece que deve ser possível receber mudanças upstream do PostgreSQL, mas, se isso fosse fácil, ela teria virado parte do projeto PostgreSQL desde o início, então não parece tão simples
“Correto. Mas há um longo caminho pela frente. No momento, o OrioleDB é uma extensão fornecida junto com patches no core do PostgreSQL. O objetivo de médio prazo é transformar o OrioleDB em uma extensão pura, e o objetivo de longo prazo é tornar o OrioleDB parte do core do PostgreSQL”
Da próxima vez, seria melhor não chegar tão forte logo de início
https://news.ycombinator.com/item?id=36742001
Ver um exemplo corrigido certamente ajuda. Para uma empresa que tenha recursos para manter um fork do Postgres, ótimo; caso contrário, é preciso avaliar se a dificuldade de usar esse fork compensa em relação aos ganhos de desempenho
Já chega dessa história de que “a Uber migrou do Postgres para o MySQL”. A Uber saiu do Postgres que usava como banco de dados relacional para algo que, na prática, era seu próprio banco de dados não relacional, usando MySQL como armazenamento distribuído de chave-valor, então não é uma situação que se aplique diretamente à maioria dos usuários de Postgres
De todo modo, esse desenho de MVCC que move dados antigos para logs/segmentos de undo também é usado no Oracle DB, então o funcionamento em si já foi validado
A dificuldade comum é que ler versões antigas dos dados fica mais lento. É preciso procurá-las no log e, às vezes, os dados podem ser removidos do log antes de a transação terminar, causando o famigerado erro Snapshot Too Old
Não vi no texto quando as linhas são removidas do log de undo. Se forem removidas quando não forem mais necessárias, parece que seria preciso uma quantidade parecida de trabalho de gerenciamento, e não está claro para mim de onde viria a melhoria. Se for um buffer circular que pode ficar sem espaço, como no Oracle, então transações longas começam a falhar sob alta carga de escrita, o que é bem desagradável
Depende do que exatamente se quer dizer com “trabalho de gerenciamento”. Se olharmos para o volume de trabalho, registros antigos de undo podem ser removidos em lote de forma muito barata, por exemplo dando
unlinkem arquivos, e é daí que vem a melhoria. Não é necessário fazer varredura de VACUUMSe olharmos para uso de espaço, é verdade que o mesmo número de versões ocupa a mesma quantidade de espaço. Mas manter versões antigas de linhas em um armazenamento separado evita a degradação de longo prazo do armazenamento principal. O OrioleDB também implementa mesclagem automática de páginas esparsas
O OrioleDB implementa um buffer circular em memória para o log de undo. Se o buffer circular não comportar todos os registros de undo, os registros mais antigos são despejados para o armazenamento. Atualmente não há limite para o tamanho do log de undo, e os registros são mantidos enquanto qualquer transação puder precisar deles, portanto não há erro “Snapshot Too Old”
Ainda assim, dá para considerar implementar, como opção, um erro no estilo Oracle para permitir limitar o tamanho do undo. Também vale conferir o documento de arquitetura no GitHub
https://github.com/orioledb/orioledb/blob/main/doc/arch.md
Claro que o MySQL evita VACUUM abrindo mão de bastante coisa em termos de concorrência
O título me deixou animado, mas a primeira impressão foi frustrante. O README principal no GitHub[1] parece texto promocional corporativo
Até agora, o que consegui entender é que o OrioleDB é um novo mecanismo de armazenamento para PostgreSQL, que o PostgreSQL é o banco de dados “mais amado”, que o OrioleDB é uma extensão construída sobre outras extensões e que o OrioleDB abre as portas da nuvem
Depois da onda de golpes com criptomoedas e Web 3.0, eu teria evitado esse tipo de abordagem ao lançar um projeto para estender e melhorar algo tão importante quanto o PostgreSQL
[1] https://github.com/orioledb/orioledb
Acho difícil escrever de forma mais clara que isso. Métodos de acesso a tabelas são um recurso introduzido no PostgreSQL para dar suporte a mecanismos alternativos de armazenamento, como zheap, ou a armazenamentos orientados a colunas
Mencionar esse fato é importante. Existem muitos forks do PostgreSQL com sistemas alternativos de armazenamento de dados acoplados, enquanto o OrioleDB foi projetado para funcionar como extensão em um PostgreSQL sem fork. Ainda não é assim, mas essa é a direção
Para quem conhece PostgreSQL, o README parece bem claro
O design do Oriole parece exigir índices cientes de transações e remoção de entradas ponto a ponto, e isso tem seu próprio custo
Por exemplo, algo equivalente ao GiST para índices espaciais parece trabalhoso de manter, pela natureza de não se saber exatamente a localização de cada tupla de índice, e o GIN para indexação de busca em texto completo poderia ficar muito grande por perder capacidade de compressão nas árvores de postings
Além disso, em um cenário com tabelas organizadas por índice, não consigo imaginar bem como implementar algo equivalente ao BRIN. O BRIN permite excluir rapidamente grandes intervalos da tabela física dos resultados da consulta quando eles não contêm dados de interesse. Seria possível particionar por intervalo de chave primária, mas a densidade de valores da chave primária pode variar muito ao longo do tempo e entre faixas de valores
Tenho curiosidade sobre como pretendem implementar esses métodos de índice mais complexos, mas muito úteis
Há também problemas que podem surgir quando regras de ordenação, isto é, collations, mudam. Como o heap e o VACUUM do Postgres não conhecem a ordem de classificação, muitas vezes é possível corrigir danos após uma mudança de collation apagando e reinserindo linhas que ficaram em posições incorretas, e deixando o VACUUM limpar eventualmente as tuplas quebradas
No Oriole, parece difícil fazer isso porque não daria para localizar por busca pontual a tupla original a ser removida, e corrigir casos conhecidos de corrupção de índice provavelmente exigiria reconstruir o índice inteiro, o que parece aumentar bastante o ônus de manutenção
Em uma funcionalidade semelhante ao GIN, a compressão de listas de postings ainda é possível. Uma opção viável é vincular registros de undo à lista de postings inteira, em vez de a um único item da lista de postings
Como o BRIN usa tabelas organizadas por índice, não parece que será possível criar um análogo direto. Ainda assim, dá para fazer coisas interessantes com union keys nas páginas internas da chave primária
O problema de collation é real e sério. Antes do GA, todos os índices sensíveis a collation provavelmente terão de ser fixados a uma versão específica de collation da libicu
A lógica do texto também é convincente, e os benchmarks parecem sustentar as alegações de desempenho, mas não entendo bem a distinção entre o novo mecanismo de armazenamento proposto — isto é, o OrioleDB — e o próprio PostgreSQL.
Tirando a motivação comercial e o desejo de lucrar com as inovações abordadas no texto, há algum motivo para comercializar isso como um novo banco de dados chamado OrioleDB, em vez de contribuir essas melhorias upstream?
Mas as mudanças do OrioleDB são grandes demais para serem incorporadas gradualmente. Por isso, a comparação está sendo feita entre o OrioleDB e o engine atual do PostgreSQL, incluindo não só o heap, mas também vários subsistemas.
Para mais detalhes, veja especialmente os slides 9 a 11 em https://www.socallinuxexpo.org/sites/default/files/presentations/solving-postgres-wicked-problems.pdf
Se for uma extensão de engine, fico curioso para saber se há impacto ao usá-la junto com outras extensões. Por exemplo, timescaledb[0] funciona sobre tabelas subjacentes.
Fico me perguntando que efeito algo assim teria:
create table xyz(...) using orioledb;select create_hypertable(xyz, ts);[0] https://github.com/timescale/timescaledb
Será que o OrioleDB teria interesse em prometer um formato em disco estável, eliminando a necessidade de um procedimento de upgrade entre versões major do Postgres? Parece uma oportunidade para resolver esse problema.
Coisas como adicionar novos tipos e suas funções de suporte precisam ser inseridas por algum procedimento de upgrade. Também há outras mudanças de catálogo que alteram o layout de colunas das tabelas de catálogo, e nesse caso também é necessário um procedimento para atualizar os dados armazenados entre versões.
Sem um procedimento de upgrade, não é possível alterar o catálogo. É por isso que, no PostgreSQL, apenas upgrades de versões minor podem ser feitos só com a substituição dos binários, com rollback seguro e sem problemas.
Se as atualizações ficassem limitadas apenas a mudanças de API interna, planner e executor, o desenvolvimento ficaria seriamente restringido. Acho difícil que o OrioleDB elimine a necessidade desse procedimento de upgrade.
A maior parte do trabalho de upgrade de um cluster vem de reescrever as tabelas de catálogo. Além disso, para a maioria das cargas de trabalho, o pg_upgrade é muito rápido, então também não vejo onde estaria o grande ganho.
A expressão “redução de 2,3x na sobrecarga de CPU por transação” me incomoda. No Postgres, o uso de CPU oscila de 5% a 65%, enquanto no Oriole fica sempre em 90%
Previsibilidade é bom, mas elevar a faixa baixa em 85% não é algo bastante preocupante?
A boa notícia é que isso significa que, ao escalar verticalmente com uma CPU mais forte, dá para obter mais desempenho no Oriole. Já o Postgres não continuaria ganhando desempenho da mesma forma
Do ponto de vista de um sistema operacional de desktop, talvez dê para usar o tempo ocioso do servidor Postgres para outra coisa, mas em servidores normalmente se quer uma máquina que faça uma coisa só e seja otimizada para isso
Se o TPS for reduzido, a CPU também deve cair proporcionalmente, e aqui parece que a intenção é mostrar até onde ele consegue chegar
Se você limitar o sistema a 60% de CPU, os números absolutos podem mudar, mas, se no mesmo nível de uso o TPS for 1,8x maior, é vitória de qualquer jeito. Parece um número bem bom, mais do que um truque de marketing
Se uma CPU de servidor cara custa X dólares por unidade, mas você só consegue usar 60% dela — e, na prática, é só isso mesmo que dá para usar —, é como queimar 0,4X dólar por unidade
Se você consegue levar a carga de trabalho verticalmente até saturar 90% de uma máquina, em geral é fácil aplicar QoS e técnicas de isolamento para manter uma saturação menor com desempenho proporcional. O inverso não é verdadeiro. Se você só consegue usar 60% da saturação total da máquina e precisa escalar horizontalmente, para passar de 90% será necessário redesenhar, e foi exatamente esse redesenho que aconteceu aqui
Dizem que, como resultado acumulado das melhorias, o OrioleDB oferece TPS 5 vezes maior, carga de CPU por transação 2,3 vezes menor, IOPS por transação 22 vezes menor e ausência de bloat em tabelas e índices
A oscilação da carga de CPU não é o Postgres “escalando”, mas sim entrando periodicamente em gargalos de desempenho. Provavelmente por causa da necessidade de executar VACUUM, algo muito sensível a I/O
Por isso, em vez de usar I/O para processar consultas, o Postgres usa I/O para tarefas de limpeza, e o TPS e o uso de CPU despencam juntos
Já o Oriole gerencia uma vazão muito maior de forma bem mais consistente. Se você pisar no acelerador, qual carro preferiria: um que segue estável a 100 mph, ou um que, mesmo com o pedal no fundo, oscila violentamente entre 40 e 70 mph?
No texto há um link com um título bem interessante: “10 coisas que Richard Branson odeia no PostgreSQL”. No fim, descobri que quem escreveu aquele blog não foi Richard, e sim Rick Branson
Ah, não era ele