O PostgreSQL vem com um excelente conjunto de recursos, inigualável no espaço RDBMS de código aberto. É principalmente fácil de aprender e usar, especialmente para desenvolvedores de aplicativos. No entanto, algumas partes simplesmente não são fáceis. Eles exigem trabalho para configurar e acertar, e normalmente também são de missão crítica.
Gerenciamento de conexões
O PostgreSQL lança um novo processo, chamado de processo de backend , para lidar com cada conexão. Isso contrasta com as arquiteturas modernas de manipulação de conexão baseadas em eventloop/threadpool encontradas em outros softwares de servidor comparáveis. Gerar um processo completo leva mais tempo e recursos e se manifesta como latências de consulta aumentadas em aplicativos em que as conexões são abertas e fechadas em uma taxa alta.
Na maioria das implantações, o agrupamento de conexões é necessário em algum nível. Em um nível de aplicativo, isso pode ser usando seus recursos de linguagem/biblioteca de programação. Por exemplo, sql/DB.SetMaxIdleConns pode ser usado para aumentar a reutilização de conexão de dentro de um único aplicativo Go.
Muitas vezes, porém, você terá que usar um pool de conexão de terceiros ou uma solução de balanceamento de carga. Os poolers de conexões mantêm um pool de conexões ociosas com o servidor Postgres upstream, que são atribuídas e enviadas por proxy para conexões de clientes de entrada. Eles normalmente analisam o SQL enviado por clientes para reconhecer limites de transação e DMLs de modificação de dados para implementar recursos como pool de conexão de nível de transação e réplicas de leitura.
O PgBouncer é um pooler de conexão binária simples, leve e popular e geralmente é executado junto com o PostgreSQL no mesmo sistema.
O PgPool é mais versátil do que o PgBouncer. Ele também pode fazer balanceamento de carga e replicação, por exemplo.
O pool de conexões traz seu próprio conjunto de dores de cabeça. Em primeiro lugar, é uma parte móvel adicional que foi mantida em sua implantação. Configurar a autenticação também é um problema se você tiver clientes que usam credenciais ou mecanismos de autenticação diferentes. Alguns recursos de nível de conexão, como LISTEN/NOTIFY, instruções preparadas, tabelas temporárias e similares, podem exigir configuração extra ou alterações no lado do cliente para funcionar.
Zero downtime upgrades
A atualização do PostgreSQL entre versões secundárias (13.x -> 13.y) envolve a instalação do novo pacote e a reinicialização do processo do servidor. Embora a reinicialização do processo do servidor necessariamente interrompa todos os clientes conectados, ainda é uma solicitação razoável, já que o tempo de inatividade está vinculado à duração de uma reinicialização de serviço.
A atualização entre as versões principais (12.x -> 13.y), no entanto, é um negócio muito maior. Normalmente, quanto mais dados houver, mais doloroso será o processo.
O método mais simples, que funciona apenas para pequenas quantidades de dados (digamos dezenas de GBs), é despejar os dados da versão antiga e restaurá-los em um servidor de nova versão. Outra opção é usar pg_upgrade, que requer uma dança orquestrada envolvendo binários de ambas as versões do Postgres.
Em ambos os casos, os bancos de dados ficariam inativos por um período considerável de tempo.
Idealmente, deve ser possível replicar para um novo servidor de versão e promover o novo servidor de versão como primário. No entanto, não é possível fazer a replicação de streaming para um servidor em espera com uma versão principal diferente. A replicação lógica, embora pareça adequada para o trabalho, tem algumas armadilhas que precisam ser contornadas para garantir a replicação completa.
A maioria das soluções de alta disponibilidade para Postgres depende da replicação de streaming e, portanto, você não pode atualizar os nós em um cluster, um de cada vez.
O estado atual da arte seria usar a replicação lógica, enquanto contornava as limitações da replicação lógica e possivelmente envolvendo a restrição de recursos que os aplicativos podem usar (como DDLs) durante a fase de atualização.
Alta disponibilidade
O PostgreSQL vem com todos os recursos de baixo nível necessários para construir uma solução HA:replicação com feedback, replicação em cascata, replicação síncrona, standbys, hot standbys, promoção de standby e assim por diante. No entanto, ele não fornece uma solução de HA pronta para uso. Não há estruturas ou ferramentas para monitorar a integridade e fazer failover automaticamente para um modo de espera. Não há noção de um cluster de alta disponibilidade de vários nós.
Você terá que configurar e executar uma solução de terceiros para criar implantações Postgres de alta disponibilidade. Os favoritos atuais sãopg_auto_failover e Patroni. Enquanto o Patroni conta com um armazenamento de configuração altamente disponível como o ZooKeeper ou etcd, o pg_auto_failover pode ficar sem um.
Avaliar, implantar e testar um deles em produção leva tempo e esforço. Os playbooks de monitoramento, alerta e operações devem ser configurados e mantidos.
Gerenciamento de Inchaço
A arquitetura MVCC do PostgreSQL significa que nenhum dado é sobrescrito – modificar uma linha apenas resulta em uma nova versão da linha sendo gravada no disco. Excluir uma linha significa apenas registrar que a linha é invisível para transações futuras. Quando uma versão de linha é inacessível a partir de qualquer transação em andamento ou futura, ela não é mais útil e é chamada de “bloat”. O processo de coleta de lixo desse inchaço é chamado de “vácuo”.
O inchaço é invisível para os aplicativos e se torna apenas a dor de cabeça do DBA. Para tabelas com muitas atualizações, monitorar e gerenciar o inchaço é uma questão não trivial. O processo de autovacuum ajuda muito, mas seus limites podem precisar ser ajustados em um nível global ou tablelevel para garantir que os tamanhos das tabelas não aumentem de forma incontrolável.
Os índices também são afetados pelo inchaço, e o autovacuum não ajuda aqui. A exclusão de linhas e a atualização de colunas indexadas levam a entradas mortas nos índices. Cargas de trabalho pesadas de atualização com atualizações em colunas indexadas podem levar a índices ineficientes e em constante crescimento. Não há equivalente de vácuo para índices. A única solução é reconstruir todo o índice usando REINDEX ou usar VACUUM FULL na tabela.
Além de um único valor por tabela (pg_stat_all_tables.n_dead_tup), o Postgres não oferece nada para estimar o inchaço em uma tabela e nada para índices. A maneira mais prática ainda continua executando uma consulta assustadora do check_postgres.
O pgmetrics incorpora a consulta de check_postgres e pode produzir saída em formato JSON e CSV que inclui informações de tamanho e volume para todas as tabelas e índices; que podem ser alimentados em ferramentas de monitoramento ou automação.
pg_repack é uma alternativa popular para VACUUM FULL – ele pode fazer o mesmo trabalho, mas sem travas. Se você é forçado a fazer VACUUM FULL regularmente, é uma ferramenta de investigação obrigatória.
zheap é o novo mecanismo de armazenamento do Postgres que está em desenvolvimento há anos, que promete reduzir o inchaço por meio de atualizações in-loco.
Gerenciamento do plano de consulta
O Core PostgreSQL oferece apenas duas ferramentas rudimentares neste espaço:
- pg_stat_statements extensão para análise de consulta – fornece o total e as médias de planejamento de consulta e tempos de execução, uso de disco e memória
- auto_explain extensão, que pode imprimir planos de execução de consulta no destino de log Postgres
Enquanto as estatísticas fornecidas por pg_stat_statements são apenas o suficiente para sobreviver, usando auto_explain forçar planos em arquivos de log e, em seguida, extraí-los não é realmente mais do que um hack, especialmente comparado aos concorrentes comerciais do Postgres, que oferecem recursos de histórico de planos, linha de base e gerenciamento.
O estado atual da arte com o Postgres é extrair o arquivo de log para os planos de consulta e armazená-los em outro lugar. Mas talvez o maior problema seja não poder associar o plano de consulta com as análises correspondentes de pg_stat_statements. A maneira como o pgDash faz isso é analisar os textos da consulta SQL de pg_stat_statements e a saída de auto_explain, ajustar para o desmembramento feito por pg_stat_statements e tentar combinar os dois. Requer um analisador SQL de dialeto PostgreSQL completo.
Linha de base, definição de políticas para seleção de planos, etc. simplesmente não são possíveis no núcleo do PostgreSQL atualmente.
Existem algumas extensões por aí que são basicamente versões aprimoradas de pg_stat_statements, mas as etapas extras envolvidas no uso de uma extensão de terceiros tornam isso um desafio para a maioria das pessoas, especialmente se estiverem usando um provedor Postgres gerenciado.
Ajuste
O PostgreSQL tem uma infinidade de opções de ajuste, começando pelo under-configured-by-defaultshared_bufferssetting. Alguns são fáceis de entender e configurar, como o número de trabalhadores paralelos para várias operações (max_worker_processes, max_parallel_* etc). Outros são um pouco obscuros (wal_compression, random_page_cost etc), mas geralmente benéficos. Os mais irritantes, porém, são os que precisam de informações quantificáveis sobre a carga de trabalho.
Por exemplo, se work_mem for muito baixo, as consultas podem usar arquivos de disco temporários; se for muito alto e houver consultas simultâneas suficientes, os processos de backend do Postgres podem ser eliminados por OOM. Então, como você descobre em qual número definir?
Praticamente, especialmente com cargas de trabalho OLTP e cargas de trabalho de aplicativos da Web, é impossível prever qual seria o pico de demanda de memória para consultas. Sua melhor aposta é definir um valor razoável e, em seguida, monitorar as consultas para ver se alguma delas poderia ter se beneficiado de um valor mais alto de work_mem.
E como você faz isso? Você precisará obter a extensão auto_explain para registrar os planos de execução de cada consulta, extraia os dos arquivos de log do Postgres, examine cada plano de consulta para ver se ele usa mesclagens externas baseadas em disco ou uma varredura de heap de bitmap com blocos de heap com perdas.
Não impossível, apenas difícil.