Ao ler os primeiros passos do PostgreSQL, você vê a linha:“O servidor PostgreSQL pode lidar com várias conexões simultâneas de clientes. Para isso, ele inicia (“forks”) um novo processo para cada conexão. A partir desse ponto, o processo cliente e o novo servidor se comunicam sem intervenção do processo postgres original. Assim, o processo do servidor mestre está sempre em execução, aguardando as conexões do cliente, enquanto os processos do cliente e do servidor associado vêm e vão. ”
Ideia brilhante. E, no entanto, isso significa que cada nova conexão gera um novo processo, reservando RAM e possivelmente ficando muito pesado com várias sessões. Para evitar problemas, o postgres tem a configuração max_connections com 100 conexões padrão. Claro que você pode aumentá-lo, mas tal ação exigiria reiniciar (pg_settings.context é 'postmaster'):
t=# select name,setting,short_desc,context from pg_settings where name = 'max_connections';
-[ RECORD 1 ]--------------------------------------------------
name | max_connections
setting | 100
short_desc | Sets the maximum number of concurrent connections.
context | postmaster
Algumas leituras interessantes Uso do PgBouncer Qual é o objetivo de pular? PgBouncer Changelog Postagens contendo 'pgbouncer' no Stack Overflow Postagens marcadas como 'pgbouncer' no 2ndQuadrant E mesmo depois de aumentar - em algum momento, você pode precisar de mais conexões (claro, com urgência, como sempre na execução do prod). Por que aumentá-lo é tão desconfortável? Porque se fosse confortável, você provavelmente acabaria com um aumento espontâneo descontrolado do número até que o cluster começasse a ficar para trás. O que significa que as conexões antigas são mais lentas - então elas levam mais tempo, então você precisa de cada vez mais novas. Para evitar essa possível avalanche e adicionar alguma flexibilidade, temos superuser_reserved_connections - para poder conectar e corrigir problemas com SU quando max_connections estiver esgotado. E obviamente vemos a necessidade de algum pool de conexões. Como queremos que os novos candidatos à conexão esperem em uma fila em vez de falhar com exceção FATAL:desculpe, muitos clientes já e não arriscando o postmaster.
O pool de conexões é oferecido em algum nível por muitos “clientes” populares. Você pode usá-lo com jdbc por um bom tempo. Recentemente, o node-postgres ofereceu seu próprio node-pg-pool. Mais ou menos a implementação é simples (como é a ideia):o pooler inicia as conexões para o banco de dados e as mantém. O cliente que se conecta ao db obtém apenas uma conexão existente “compartilhada” e após fechá-la, a conexão volta ao pool. Também temos softwares muito mais sofisticados, como o pgPool. E ainda o pgbouncer é uma escolha extremamente popular para a tarefa. Por quê? Porque ele faz apenas a parte de pooling, mas faz direitinho. É grátis. É bastante simples de configurar. E você encontra isso na maioria dos maiores provedores de serviços, conforme recomendado ou usado, por exemplo, citusdata, aws, heroku e outros recursos altamente respeitados.
Então, vamos olhar mais de perto o que ele pode e como você o usa. Na minha configuração eu uso pool_mode =transaction (seção [pgbouncer]) que é uma escolha muito popular. Dessa forma, não apenas enfileiramos as conexões que excedem max_connections, mas também reutilizamos as sessões sem esperar que a conexão anterior seja fechada:
[databases]
mon = host=1.1.1.1 port=5432 dbname=mon
mons = host=1.1.1.1 port=5432 dbname=mon pool_mode = session pool_size=2 max_db_connections=2
monst = host=1.1.1.1 port=5432 dbname=mon pool_mode = statement
[pgbouncer]
listen_addr = 1.1.1.1
listen_port = 6432
unix_socket_dir = /tmp
auth_file = /pg/pgbouncer/bnc_users.txt
auth_type = hba
auth_hba_file = /pg/pgbouncer/bnc_hba.conf
admin_users = root vao
pool_mode = transaction
server_reset_query = RESET ALL; --DEALLOCATE ALL; /* custom */
ignore_startup_parameters = extra_float_digits
application_name_add_host = 1
max_client_conn = 10000
autodb_idle_timeout = 3600
default_pool_size = 100
max_db_connections = 100
max_user_connections = 100
#server_reset_query_always = 1 #uncomment if you want older global behaviour
Breve visão geral das configurações e dicas e truques mais populares:
-
server_reset_query é muito útil e importante. No modo de pool de sessão, ele “limpa” os “artefatos” da sessão anterior. Caso contrário, você teria problemas com os mesmos nomes para instruções preparadas, configurações de sessão afetando as próximas sessões e assim por diante. O padrão é DISCARD ALL, que “reinicia” todos os estados da sessão. No entanto, você pode escolher valores mais sofisticados, por exemplo, RESET ALL; DEALLOCATE ALL; esquecer apenas SET SESSION e declarações preparadas, mantendo tabelas e planos TEMP “compartilhados”. Ou o contrário - você pode querer fazer declarações preparadas “globais” de qualquer sessão. Essa configuração é factível, embora arriscada. Você precisa fazer com que o pgbouncer reutilize a sessão para todos (tornando assim um tamanho de pool muito pequeno ou avalanchando as sessões), o que não é totalmente confiável. De qualquer forma - é uma habilidade útil. Especialmente em configurações em que você deseja que as sessões do cliente eventualmente (não imediatamente) mudem para as configurações de sessão em pool configuradas. Um ponto muito importante aqui é o modo pool de sessões. Antes do 1.6, essa configuração também afetava outros modos de pool, portanto, se você confiava nela, precisa usar a nova configuração server_reset_query_always =1. Provavelmente em algum momento as pessoas desejarão que server_reset_query seja ainda mais flexível e configurável por par db/usuário ( e client_reset_query). Mas no momento da redação atual, março de 2018, não é uma opção. A ideia por trás de tornar essa configuração válida por padrão apenas para o modo de sessão era - se você compartilha a conexão no nível da transação ou da instrução - você não pode confiar na configuração da sessão.
-
Auth_type =hba. Antes do 1.7, o grande problema com o pgbouncer era a ausência de autenticação baseada em host - “firewall postgres”. Claro que você ainda tinha para conexão de cluster postgres, mas pgbouncer estava “aberto” para qualquer fonte. Agora podemos usar o mesmo hba.conf para limitar conexões para host/db/usuário com base na rede de conexão.
-
connect_query não é executado em cada “conexão” de cliente ao pgbouncer, mas sim quando o pgbouncer se conecta a uma instância do Postgres. Assim, você não pode usá-lo para definir ou substituir as configurações "padrão". No modo de sessão, as outras sessões não afetam umas às outras e, ao desconectar, a consulta de redefinição descarta tudo - para que você não precise mexer com isso. No modo de pool de transações, você espera usá-lo para substituir as configurações erroneamente definidas por outras sessões, mas não funcionará. Por exemplo. você deseja compartilhar a instrução preparada entre “sessões” no modo de transação, então você define algo como
trns = dbname=mon pool_mode = transaction connect_query = 'do $$ begin raise warning $w$%$w$, $b$new connection$b$; end; $$; prepare s(int) as select $1;'
e de fato - todo novo cliente vê as instruções preparadas (a menos que você tenha deixado server_reset_query_always ligado, então o pgbouncer o descarta no commit). Mas se algum cliente executa DISCARD s; em sua sessão, ele afeta todos os clientes nesta conexão e novos clientes que se conectam a ele não verão mais instruções preparadas. Mas se você quiser ter alguma configuração inicial para conexões postgres vindas do pgbouncer, então este é o lugar.
-
application_name_add_host foi adicionado em 1.6, tem limitação semelhante. Ele “coloca” o IP do cliente em application_name, para que você possa obter facilmente sua fonte de consulta incorreta, mas é facilmente substituído pelo simples conjunto application_name TO 'wasn'''' me'; Ainda assim, você pode “curar” isso usando visualizações - siga este post para ter a ideia ou até mesmo use essas instruções curtas. Basicamente a ideia é mostrar aos clientes; irá mostrar o IP do cliente, então você pode consultá-lo diretamente do banco de dados do pgbouncer em cada seleção de pg_stat_activity para verificar se foi redefinido. Mas é claro que usar uma configuração simples é muito mais simples e aconchegante. Apesar de não garantir o resultado...
-
pool_mode pode ser especificado como padrão, por banco de dados e por usuário - tornando-o muito flexível. Os modos de mixagem tornam o pgbouncer extremamente eficaz para pooling. Este é um recurso poderoso, mas é preciso ter cuidado ao usá-lo. Muitas vezes os usuários o usam sem entender os resultados para combinações absolutamente atômicas de por transação/por sessão/por usuário/por banco de dados/configurações globais trabalhando de forma diferente para o mesmo usuário ou banco de dados, devido aos diferentes modos de pool com pgbouncer. Esta é a caixa de fósforos que você não dá para crianças sem supervisão. Também muitas outras opções são configuráveis por padrão e por db e por usuário.
-
Por favor, não leve isso literalmente, mas você pode “comparar” diferentes seções de ini com SET e ALTER:SET LOCAL afeta transações e é bom para usar quando poll_mode=transaction , SET SESSION afeta sessões e é seguro para uso quando poll_mode=session , ALTER USER SET afeta os papéis e interferirá na parte pgbouncer.ini da seção [usuários], ALTER DATABASE SET afeta os bancos de dados e interferirá na parte pgbouncer.ini da seção [bancos de dados], ALTER SYSTEM SET ou editar postgres.conf afeta globalmente os padrões e é comparável por efeito à seção padrão do pgbouncer.ini.
-
Mais uma vez - use o modo pool com responsabilidade. Declarações preparadas ou configurações de toda a sessão serão uma bagunça no modo de pool de transações. O mesmo que a transação SQL não faz sentido no modo de agrupamento de instruções. Escolha um modo de pool adequado para conexões adequadas. Uma boa prática é criar funções com a ideia de que:
- alguns executarão apenas seleções rápidas, portanto, podem compartilhar uma sessão sem transações para centenas de seleções pequenas e não importantes simultâneas.
- Alguns membros da função são seguros para simultaneidade no nível da sessão e SEMPRE usam transações. Assim, eles podem compartilhar com segurança várias sessões para centenas de transações simultâneas.
- Alguns papéis são muito confusos ou complicados para compartilhar sua sessão com outras pessoas. Então você usa o modo de pool de sessões para evitar erros na conexão quando todos os “slots” já estiverem ocupados.
-
Não o use em vez de HAProxy ou algum outro balanceador de carga. Apesar do pgbouncer ter vários recursos configuráveis que abordam o que um balanceador de carga endereça, como dns_max_ttl e você pode definir uma configuração de DNS para ele, a maioria dos ambientes de produção usa HAProxy ou algum outro balanceador de carga para HA. Isso ocorre porque o HAProxy é realmente bom no balanceamento de carga em servidores ativos no modo round robin, melhor que o pgbouncer. Embora o pgbouncer seja melhor para o pool de conexões postgres, pode ser melhor usar um pequeno daemon que execute perfeitamente uma tarefa, em vez de um maior que faça duas tarefas, mas pior.
-
As alterações de configuração podem ser complicadas. Algumas mudanças no pgbouncer.ini requerem reinicialização (listen_port e outros), enquanto outras como admin_users requerem recarregamento ou SIGHUP. Mudanças dentro de auth_hba_file requerem recarregamento, enquanto mudanças em auth_file não.
A visão geral extremamente curta das configurações acima é limitada pelo formato. Convido você a dar uma olhada na lista completa. O Pgbouncer é o tipo de software com uma quantidade muito pequena de “configurações chatas” - todas elas têm um enorme potencial e são de grande interesse.
Baixe o whitepaper hoje PostgreSQL Management &Automation with ClusterControlSaiba o que você precisa saber para implantar, monitorar, gerenciar e dimensionar o PostgreSQLBaixe o whitepaper
E, por último, passar de uma breve revisão entusiasmada para algo em que você possa ficar menos feliz - a instalação. O processo é claramente descrito nesta seção da documentação. A única opção descrita é construir a partir de fontes git. Mas todo mundo sabe que existem pacotes! Tentando os dois mais populares:
sudo yum install pgbouncer
sudo apt-get install pgbouncer
pode trabalhar. Mas às vezes você tem que fazer um passo extra. Por exemplo, quando nenhum pacote pgbouncer estiver disponível, tente isso.
Ou ainda:
sudo yum install pgbouncer
Loaded plugins: priorities, update-motd, upgrade-helper
amzn-main | 2.1 kB 00:00:00
amzn-updates | 2.5 kB 00:00:00
docker-ce-edge | 2.9 kB 00:00:00
docker-ce-stable | 2.9 kB 00:00:00
docker-ce-test | 2.9 kB 00:00:00
pgdg10 | 4.1 kB 00:00:00
pgdg95 | 4.1 kB 00:00:00
pgdg96 | 4.1 kB 00:00:00
pglogical | 3.0 kB 00:00:00
sensu | 2.5 kB 00:00:00
(1/3): pgdg96/x86_64/primary_db | 183 kB 00:00:00
(2/3): pgdg10/primary_db | 151 kB 00:00:00
(3/3): pgdg95/x86_64/primary_db | 204 kB 00:00:00
50 packages excluded due to repository priority protections
Resolving Dependencies
--> Running transaction check
---> Package pgbouncer.x86_64 0:1.8.1-1.rhel6 will be installed
--> Processing Dependency: libevent2 >= 2.0 for package: pgbouncer-1.8.1-1.rhel6.x86_64
--> Processing Dependency: c-ares for package: pgbouncer-1.8.1-1.rhel6.x86_64
--> Processing Dependency: libcares.so.2()(64bit) for package: pgbouncer-1.8.1-1.rhel6.x86_64
--> Running transaction check
---> Package c-ares.x86_64 0:1.13.0-1.5.amzn1 will be installed
---> Package pgbouncer.x86_64 0:1.8.1-1.rhel6 will be installed
--> Processing Dependency: libevent2 >= 2.0 for package: pgbouncer-1.8.1-1.rhel6.x86_64
--> Finished Dependency Resolution
Error: Package: pgbouncer-1.8.1-1.rhel6.x86_64 (pgdg10)
Requires: libevent2 >= 2.0
You could try using --skip-broken to work around the problem
You could try running: rpm -Va --nofiles --nodigest
É claro que adicionar pgdg ao /etc/yum.repos.d/ não ajudará mais. Nem o --skip-broken ou rpm -Va --nofiles --nodigest. Um simples
sudo yum install libevent2
Loaded plugins: priorities, update-motd, upgrade-helper
50 packages excluded due to repository priority protections
No package libevent2 available.
Error: Nothing to do
seria muito fácil. Então você tem que construir o libevent2 por conta própria, trazendo você de volta à posição em que você mesmo tem que compilar as coisas. Ou é pgbouncer ou uma de suas dependências.
Novamente - cavar muito fundo com as particularidades da instalação está fora do escopo. Você deve saber que tem uma grande chance de instalá-lo como pacote.
Por fim, perguntas como “por que o postgres não oferece um pool de sessão nativo” são recorrentes. Há até mesmo sugestões e pensamentos muito novos sobre isso. Mas até agora a abordagem mais popular aqui é usar o pgbouncer.