sql >> Base de Datos >  >> RDS >> PostgreSQL

Agrupación de conexiones de PostgreSQL con PgBouncer

La agrupación de conexiones es una forma simple pero efectiva de mejorar el rendimiento de sus aplicaciones y disminuir la carga en sus servidores PostgreSQL. Siga leyendo para obtener más información sobre el uso de PgBouncer para agrupar conexiones de PostgreSQL.

¿Por qué agrupar conexiones?

PostgreSQL tiene una arquitectura de manejo de conexiones bastante pesada. Para cada conexión entrante, el postmaster (el demonio principal de Postgres) bifurca un nuevo proceso (convencionalmente llamado backend ) para manejarlo. Si bien este diseño brinda mejor estabilidad y aislamiento, no lo hace particularmente eficiente en el manejo de conexiones de corta duración. Una nueva conexión de cliente de Postgres implica la configuración de TCP, la creación de procesos y la inicialización de back-end, todo lo cual es costoso en términos de tiempo y recursos del sistema.

Por supuesto, esto solo es un problema si las conexiones se crean con demasiada frecuencia y se descartan sin reutilizarlas. Desafortunadamente, no es raro tener un grupo de nodos web que ejecutan aplicaciones escritas en PHP u otros lenguajes similares que necesitan conectarse a la base de datos una vez por carga de página. También son comunes los trabajos por lotes que realizan rápidamente un montón de conexiones en rápida sucesión. Empleando conexión compartida en tales escenarios puede reducir drásticamente la carga en su servidor PostgreSQL y mejorar drásticamente las latencias de consulta.

Con la agrupación de conexiones, los clientes se conectan a un servidor proxy que mantiene un conjunto de conexiones directas al servidor PostgreSQL real. Por lo general, los clientes no se dan cuenta (y no deberían hacerlo) de que están conectados a un servidor proxy en lugar del servidor real. El proxy puede ejecutarse en el mismo nodo que el cliente (por ejemplo, en cada nodo web), en cuyo caso los clientes pueden conectarse al proxy a través de sockets de dominio Unix que tienen una sobrecarga de conexión muy baja. Incluso si el proxy está en otro nodo y el cliente necesita una conexión TCP para llegar al proxy, se puede evitar la sobrecarga de un nuevo backend de Postgres.

¿Qué es PgBouncer?

PgBouncer es un agrupador de conexiones de un solo binario, ligero y de código abierto para PostgreSQL. Puede agrupar conexiones a una o más bases de datos (posiblemente en diferentes servidores) y servir a los clientes a través de sockets de dominio TCP y Unix.

PgBouncer mantiene un conjunto de conexiones para cada usuario único, par de base de datos. Por lo general, está configurado para entregar una de estas conexiones a una nueva conexión de cliente entrante y devolverla al conjunto cuando el cliente se desconecta. Puede configurar PgBouncer para que se agrupe de manera más agresiva, de modo que pueda recoger y devolver la conexión al grupo en los límites de transacción o declaración en lugar de los límites de conexión. Sin embargo, hay algunas consecuencias potencialmente indeseables.

Debería poder instalar PgBouncer con el administrador de paquetes de su distribución:

# RedHat/CentOS/..
$ sudo yum install pgbouncer

# Debian/Ubuntu/..
$ sudo apt-get install pgbouncer

También está disponible en los repositorios APT y YUM estándar de Postgres, que se pueden usar si los paquetes de su distribución son antiguos o están dañados.

PgBouncer se basa en un archivo de configuración principal, normalmente almacenado como /etc/pgbouncer/pgbouncer.ini . Puede invocar pgbouncer como un servicio systemd, o simplemente ejecutarlo incluso sin privilegios de superusuario con la ruta a este archivo de configuración.

Para darle una vuelta, creemos una base de datos db1 y un usuario usuario1 en nuestro servidor:

$ sudo -u postgres psql
psql (10.6 (Debian 10.6-1.pgdg90+1))
Type "help" for help.

postgres=# create user user1 password 'user1pass';
CREATE ROLE
postgres=# create database db1 owner user1;
CREATE DATABASE
postgres=#

Los clientes se conectarán a la base de datos db1 con el nombre de usuario user1 y contraseña user1pass . Nuestro objetivo es lograr que los clientes se conecten a PgBouncer, que actuará como proxy y agrupará las conexiones con el servidor real.

Ahora vamos a crear un archivo (en cualquier lugar) con estos contenidos:

[databases]
db1 = host=localhost dbname=db1

[pgbouncer]
listen_addr = 127.0.0.1
listen_port = 16432
auth_file = userlist.txt

También necesitamos crear un archivo "userlist.txt" en el mismo directorio, con el nombre de usuario y las contraseñas (hash) de los usuarios que PgBouncer permitirá conectarse. Cree "userlist.txt" con los siguientes contenidos:

"user1" "md5638b81c77071ea624d1ad4adb1433540"

El segundo valor es el MD5 de "user1passuser1", con el prefijo "md5". Esta es la convención habitual de Postgres.

Ahora comencemos PgBouncer en primer plano:

$ /usr/sbin/pgbouncer pgbouncer.ini
2019-02-05 11:46:18.011 10033 LOG file descriptor limit: 1024 (H:1048576), max_client_conn: 100, max fds possible: 130
2019-02-05 11:46:18.012 10033 LOG listening on 127.0.0.1:16432
2019-02-05 11:46:18.013 10033 LOG listening on unix:/tmp/.s.PGSQL.16432
2019-02-05 11:46:18.014 10033 LOG process up: pgbouncer 1.9.0, libevent 2.0.21-stable (epoll), adns: c-ares 1.12.0, tls: OpenSSL 1.1.0j  20 Nov 2018

Ahora hemos iniciado un PgBouncer que escucha en el puerto TCP 127.0.0.1 16432, así como en el socket de dominio Unix /tmp/.s.PGSQL.16432 . La única "base de datos" disponible en este servidor proxy es db1 . El único usuario que puede conectarse a este servidor es user1 . Intentemos conectarnos con psql :

$ psql -U user1 -p 16432 -h localhost db1
Password for user user1:
psql (10.6 (Debian 10.6-1.pgdg90+1))
Type "help" for help.

db1=> select inet_server_addr(), inet_server_port();
 inet_server_addr | inet_server_port
------------------+------------------
 127.0.0.1        |             5432
(1 row)

db1=>

El cliente (psql) se conecta con éxito al host local:16432, pero puede ver que la conexión en realidad se está transmitiendo al host local:5432.

Puede intentar desconectarse y conectarse nuevamente varias veces, luego verifique cuántas conexiones quedan en el servidor actual:

postgres=# select count(*) from pg_stat_activity
postgres-#   where datname='db1' and usename='user1';
 count
-------
     1
(1 row)

PgBouncer no desconectará la conexión real cuando el cliente se desconecte. Puede configurar las conexiones mínimas, máximas y reservadas que PgBouncer mantendrá para cada grupo en el archivo de configuración.

Implementación de PgBouncer

¿Dónde instala y ejecuta PgBouncer? Hay diferentes respuestas, con diferentes ventajas:

  • En el nodo del servidor de Postgres :Puede instalarlo junto con el propio servidor PostgreSQL, en el mismo nodo. Los clientes se conectan al puerto de PgBouncer en lugar del puerto de Postgres. Esto tiene el efecto de un Postgres "mejorado" que agrupa las conexiones internamente. También solo tiene que mantener una copia de los archivos de configuración para PgBouncer. Por otro lado, esto implica ejecutar algo más también en el nodo del servidor PostgreSQL, lo que puede no ser fácil o no estar permitido (cortafuegos, políticas) o incluso posible (AWSRDS).
  • En nodos de cliente :Puede instalar PgBouncer en cada nodo cliente, por ejemplo, cada nodo web ejecuta Apache y PHP, y los scripts de PHP se conectan al PgBouncer local. Esto tiene la ventaja de no tener que alterar la configuración del servidor, y la configuración del grupo se puede usar para mantener la carga del servidor predecible. Por otro lado, si la cantidad de nodos del cliente es enorme o puede variar mucho según la carga/ tráfico, el servidor puede sobrecargarse rápidamente.
  • Como un clúster independiente :La tercera opción para tener un grupo de nodos PgBouncer independientes y sin estado, encabezados por un balanceador de carga TCP como HAProxy. Esta configuración, aunque es más complicada que las otras dos opciones, proporciona el máximo control y capacidad de configuración.

Administración

PgBouncer permite a los usuarios marcados como administradores conectarse a una base de datos virtual llamada “pgbouncer” y emitir comandos para controlar el servidor y ver estadísticas. Para probar esto, primero marquemos "usuario1" como administrador modificando el archivo pgbouncer.ini:

[databases]
db1 = host=localhost dbname=db1

[pgbouncer]
listen_addr = 127.0.0.1
listen_port = 16432
auth_file = userlist.txt
admin_users = user1

Ahora el usuario1 puede conectarse a la base de datos llamada "pgbouncer":

$ psql -U user1 -p 16432 -h localhost pgbouncer
Password for user user1:
psql (10.6 (Debian 10.6-1.pgdg90+1), server 1.9.0/bouncer)
Type "help" for help.

pgbouncer=#

Desde aquí, puede hacer varias cosas, como habilitar o deshabilitar una base de datos en particular, inspeccionar y volver a cargar la configuración, y más:

pgbouncer=# RELOAD;
RELOAD
pgbouncer=# DISABLE db1;
DISABLE
pgbouncer=# ENABLE db1;
ENABLE
pgbouncer=# SHOW FDS;
 fd |  task  | user  | database |   addr    | port  |     cancel     | link | client_encoding | std_strings | datestyle | timezone  | pa
----+--------+-------+----------+-----------+-------+----------------+------+-----------------+-------------+-----------+-----------+---
  6 | pooler |       |          | 127.0.0.1 | 16432 |              0 |    0 |                 |             |           |           |
  7 | pooler |       |          | unix      | 16432 |              0 |    0 |                 |             |           |           |
  9 | server | user1 | db1      | 127.0.0.1 |  5432 | 45404395804679 |    0 | UTF8            | on          | ISO, MDY  | localtime |
(3 rows)

Supervisión

También hay comandos para mostrar varias estadísticas sobre PgBouncer, que incluyen:

  • Estadísticas por base de datos sobre la duración de la consulta, el tiempo de espera del cliente, el uso de la red, el recuento de transacciones
  • Estadísticas por grupo sobre el número de clientes activos y en espera, conexiones de servidor inactivas y usadas

Las estadísticas se recuperan con comandos de estilo "MOSTRAR xyz", como este para obtener estadísticas relacionadas con el grupo:

pgbouncer=# SHOW POOLS;
-[ RECORD 1 ]---------
database   | db1
user       | user1
cl_active  | 0
cl_waiting | 0
sv_active  | 0
sv_idle    | 0
sv_used    | 1
sv_tested  | 0
sv_login   | 0
maxwait    | 0
maxwait_us | 0
pool_mode  | session
-[ RECORD 2 ]---------
database   | pgbouncer
user       | pgbouncer
cl_active  | 1
cl_waiting | 0
sv_active  | 0
sv_idle    | 0
sv_used    | 0
sv_tested  | 0
sv_login   | 0
maxwait    | 0
maxwait_us | 0
pool_mode  | statement

Lecturas adicionales

La página de inicio de PgBouncer tiene más detalles sobre todas las funciones y opciones de configuración de PgBouncer.

  • Página de inicio de PgBouncer
  • Repositorio PgBouncer GitHub
  • Postgres Wiki tiene información sobre la agrupación de conexiones
  • Pgpool es otra opción para la agrupación de conexiones