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

Una guía de Pgpool para PostgreSQL:primera parte

Pgpool es menos actual hoy que hace 10 años, cuando era la parte predeterminada de una configuración de producción de PostgreSQL. A menudo, cuando alguien hablaba del clúster de PostgreSQL, se refería a PostgreSQL detrás de pgpool y no a la instancia de PostgreSQL en sí (que es el término correcto). Pgpool es reconocido entre los jugadores de Postgres más influyentes:comunidad postgresql, commandprompt, 2ndquadrant, EDB, citusdata, postgrespro (ordenados por edad, no por influencia). Me doy cuenta de que el nivel de reconocimiento en mis enlaces es muy diferente; solo quiero enfatizar el impacto general de pgpool en el mundo de postgres. Algunos de los “proveedores” de postgres actuales más conocidos se encontraron después de que el pgpool ya fuera famoso. Entonces, ¿qué lo hace tan famoso?

Solo la lista de las funciones ofrecidas más solicitadas hace que se vea genial:

  • replicación nativa
  • agrupación de conexiones
  • equilibrio de carga para escalabilidad de lectura
  • alta disponibilidad (perro guardián con IP virtual, recuperación en línea y conmutación por error)

Bueno, hagamos un arenero y juguemos. Mi configuración de muestra es el modo maestro esclavo. Supongo que es el más popular hoy en día, porque normalmente usa la replicación de transmisión junto con el equilibrio de carga. El modo de replicación apenas se usa en estos días. La mayoría de los administradores de bases de datos lo saltan a favor de replicación de transmisión y pglogical, y anteriormente a slony.

El modo de replicación tiene muchas configuraciones interesantes y una funcionalidad seguramente interesante. Pero la mayoría de los DBA tienen una configuración de maestro/esclavo múltiple cuando llegan a pgpool. Por lo tanto, están buscando una conmutación por error automática y un balanceador de carga, y pgpool lo ofrece listo para usar para entornos maestros/esclavos múltiples existentes. Sin mencionar que a partir de Postgres 9.4, la replicación de transmisión funciona sin errores importantes y se admite la replicación de 10 índices hash, por lo que apenas hay nada que le impida usarlo. Además, la replicación de transmisión es asíncrona de forma predeterminada (configurable para configuraciones complicadas de sincronización síncrona e incluso no "lineal", mientras que la replicación nativa de pgpool es síncrona (lo que significa cambios de datos más lentos) sin opción de elección. También se aplican limitaciones adicionales. El propio manual de Pgpool sugiere preferir cuando sea posible, replicación de transmisión a través de pgpool nativo). Y entonces esta es mi elección aquí.

Ah, pero primero tenemos que instalarlo, ¿verdad?

Instalación (de una versión superior en ubuntu).

Primero verificando la versión de ubuntu con lsb_release -a. Para mí, el repositorio es:

[email protected]:~# sudo add-apt-repository 'deb http://apt.postgresql.org/pub/repos/apt/ xenial-pgdg main'
wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | \
>   sudo apt-key add -
OK
[email protected]:~# sudo apt-get update

Por último, la instalación en sí:

sudo apt-get install pgpool2=3.7.2-1.pgdg16.04+1

Configuración:

Yo uso la configuración predeterminada del modo recomendado:

zcat /usr/share/doc/pgpool2/examples/pgpool.conf.sample-stream.gz > /etc/pgpool2/pgpool.conf

Inicio:

Si te perdiste la configuración, verás:

2018-03-22 13:52:53.284 GMT [13866] FATAL:  role "nobody" does not exist

Ah, es cierto:mi error, pero se puede corregir fácilmente (se puede hacer a ciegas con un delineador si desea que el mismo usuario realice todos los controles de salud y recuperación):

[email protected]:~# sed -i s/'nobody'/'pgpool'/g /etc/pgpool2/pgpool.conf

Y antes de continuar, creemos la base de datos pgpool y el usuario pgpool en todos los clústeres (en mi sandbox son maestro, conmutación por error y esclavo, por lo que necesito ejecutarlo solo en el maestro):

t=# create database pgpool;
CREATE DATABASE
t=# create user pgpool;
CREATE ROLE

Por fin - comenzando:

[email protected]:~$ /usr/sbin/service pgpool2 start
[email protected]:~$ /usr/sbin/service pgpool2 status
pgpool2.service - pgpool-II
   Loaded: loaded (/lib/systemd/system/pgpool2.service; enabled; vendor preset: enabled)
   Active: active (running) since Mon 2018-04-09 10:25:16 IST; 4h 14min ago
     Docs: man:pgpool(8)
  Process: 19231 ExecReload=/bin/kill -HUP $MAINPID (code=exited, status=0/SUCCESS)
 Main PID: 8770 (pgpool)
    Tasks: 10
   Memory: 5.5M
      CPU: 18.250s
   CGroup: /system.slice/pgpool2.service
           ├─ 7658 pgpool: wait for connection reques
           ├─ 7659 pgpool: wait for connection reques
           ├─ 7660 pgpool: wait for connection reques
           ├─ 8770 /usr/sbin/pgpool -n
           ├─ 8887 pgpool: PCP: wait for connection reques
           ├─ 8889 pgpool: health check process(0
           ├─ 8890 pgpool: health check process(1
           ├─ 8891 pgpool: health check process(2
           ├─19915 pgpool: postgres t ::1(58766) idl
           └─23730 pgpool: worker proces

Genial, para que podamos continuar con la primera función, verifiquemos el equilibrio de carga. Tiene algunos requisitos para ser utilizado, admite sugerencias (por ejemplo, para equilibrar en la misma sesión), tiene funciones en lista blanca y negra, tiene una lista de preferencias de redirección basada en expresiones regulares. es sofisticado Por desgracia, repasar a fondo toda esa funcionalidad estaría fuera del alcance de este blog, por lo que revisaremos las demostraciones más simples:

Primero, algo muy simple mostrará qué nodo se usa para seleccionar (en mi configuración, el maestro gira en 5400, el esclavo en 5402 y la conmutación por error en 5401, mientras que pgpool en sí está en 5433, ya que tengo otro clúster en ejecución y no quería interferir con él):

[email protected]:~$ psql -h localhost -p 5433 t -c "select current_setting('port') from ts limit 1"
 current_setting
-----------------
 5400
(1 row)

Luego en bucle:

[email protected]:~$ (for i in $(seq 1 99); do psql -h localhost -p 5433 t -c "select current_setting('port') from ts limit 1" -XAt; done) | sort| uniq -c
      9 5400
     30 5401
     60 5402

Estupendo. Definitivamente equilibra la carga entre los nodos, pero parece que no lo hace por igual. ¿Tal vez es tan inteligente que conoce el peso de cada declaración? Comprobemos la distribución con los resultados esperados:

t=# show pool_nodes;
 node_id | hostname  | port | status | lb_weight |  role   | select_cnt | load_balance_node | replication_delay
---------+-----------+------+--------+-----------+---------+------------+-------------------+-------------------
 0       | localhost | 5400 | up     | 0.125000  | primary | 122        | false             | 0
 1       | localhost | 5401 | up     | 0.312500  | standby | 169        | false             | 0
 2       | localhost | 5402 | up     | 0.562500  | standby | 299        | true              | 0
(3 rows)

No, pgpool no analiza el peso de las declaraciones, ¡era un DBA con su configuración nuevamente! La configuración (consulte el atributo lb_weight) se concilia con los objetivos de destino de consulta reales. Puede cambiarlo fácilmente (como lo hicimos aquí) cambiando la configuración correspondiente, por ejemplo:

[email protected]:~$ grep weight /etc/pgpool2/pgpool.conf
backend_weight0 =0.2
backend_weight1 = 0.5
backend_weight2 = 0.9
[email protected]:~# sed -i s/'backend_weight2 = 0.9'/'backend_weight2 = 0.2'/ /etc/pgpool2/pgpool.conf
[email protected]:~# grep backend_weight2 /etc/pgpool2/pgpool.conf
backend_weight2 = 0.2
[email protected]:~# pgpool reload
[email protected]:~$ (for i in $(seq 1 9); do psql -h localhost -p 5433 t -c "select current_setting('port') from ts limit 1" -XAt; done) | sort| uniq -c
      6 5401
      3 5402
Descargue el documento técnico hoy Gestión y automatización de PostgreSQL con ClusterControl Obtenga información sobre lo que necesita saber para implementar, monitorear, administrar y escalar PostgreSQLDescargar el documento técnico

¡Estupendo! La siguiente gran característica que se ofrece es la agrupación de conexiones. Con 3.5, el "problema del rebaño atronador" se resuelve mediante la serialización de llamadas accept(), lo que acelera enormemente el tiempo de "conexión del cliente". Y, sin embargo, esta función es bastante sencilla. No ofrece varios niveles de agrupación o varios grupos configurados para la misma base de datos (sin embargo, pgpool le permite elegir dónde ejecutar selecciones con database_redirect_preference_list de balanceo de carga), u otras funciones flexibles que ofrece pgBouncer.

Demostración tan breve:

t=# select pid,usename,backend_type, state, left(query,33) from pg_stat_activity where usename='vao' and pid <> pg_backend_pid();
 pid  | usename |  backend_type  | state |     left
------+---------+----------------+-------+--------------
 8911 | vao     | client backend | idle  |  DISCARD ALL
 8901 | vao     | client backend | idle  |  DISCARD ALL
 7828 | vao     | client backend | idle  |  DISCARD ALL
 8966 | vao     | client backend | idle  |  DISCARD ALL
(4 rows)
Hm - did I set up this little number of children?
t=# pgpool show num_init_children;
 num_init_children
-------------------
 4
(1 row)

Ah, cierto, los cambié por debajo de los 32 predeterminados, por lo que la salida no tomaría varias páginas. Entonces, intentemos exceder el número de sesiones (abajo abro sesiones de postgres asíncronas en bucle, por lo que las 6 sesiones se solicitarían más o menos al mismo tiempo):

[email protected]:~$ for i in $(seq 1 6); do (psql -h localhost -p 5433 t -U vao -c "select pg_backend_pid(), pg_sleep(1), current_setting('port'), clock_timestamp()" &);  done
[email protected]:~$  pg_backend_pid | pg_sleep | current_setting |        clock_timestamp
----------------+----------+-----------------+-------------------------------
           8904 |          | 5402            | 2018-04-10 12:46:55.626206+01
(1 row)

 pg_backend_pid | pg_sleep | current_setting |        clock_timestamp
----------------+----------+-----------------+-------------------------------
           9391 |          | 5401            | 2018-04-10 12:46:55.630175+01
(1 row)

 pg_backend_pid | pg_sleep | current_setting |       clock_timestamp
----------------+----------+-----------------+------------------------------
           8911 |          | 5400            | 2018-04-10 12:46:55.64933+01
(1 row)

 pg_backend_pid | pg_sleep | current_setting |        clock_timestamp
----------------+----------+-----------------+-------------------------------
           8904 |          | 5402            | 2018-04-10 12:46:56.629555+01
(1 row)

 pg_backend_pid | pg_sleep | current_setting |        clock_timestamp
----------------+----------+-----------------+-------------------------------
           9392 |          | 5402            | 2018-04-10 12:46:56.633092+01
(1 row)

 pg_backend_pid | pg_sleep | current_setting |       clock_timestamp
----------------+----------+-----------------+------------------------------
           8910 |          | 5402            | 2018-04-10 12:46:56.65543+01
(1 row)

Permite que las sesiones lleguen en tres, como se esperaba, ya que la sesión anterior toma una (seleccionando de pg_stat_activity), por lo que 4-1 =3. Tan pronto como pg_sleep termina su segunda siesta y postgres cierra la sesión, se deja entrar al siguiente. Entonces, después de que terminan los primeros tres, entran los siguientes tres. ¿Qué sucede con el resto? Se ponen en cola hasta que se libera la siguiente ranura de conexión. Luego ocurre el proceso descrito junto a serialize_accept y el cliente se conecta.

¿Eh? ¿Solo agrupación de sesiones en modo de sesión? ¿Es todo?... No, ¡aquí interviene el almacenamiento en caché! Mira.:

postgres=# /*NO LOAD BALANCE*/ select 1;
 ?column?
----------
        1
(1 row)

Comprobando la pg_stat_actividad:

postgres=# select pid, datname, state, left(query,33),state_change::time(0), now()::time(0) from pg_stat_activity where usename='vao' and query not like '%DISCARD%';
  pid  | datname  | state |               left                | state_change |   now
-------+----------+-------+-----------------------------------+--------------+----------
 15506 | postgres | idle  | /*NO LOAD BALANCE*/ select 1, now | 13:35:44     | 13:37:19
(1 row)

Luego ejecute la primera declaración nuevamente y observe que state_change no cambia, lo que significa que ni siquiera llega a la base de datos para obtener un resultado conocido. Por supuesto, si coloca alguna función mutable, los resultados no se almacenarán en caché. Experimenta con:

postgres=# /*NO LOAD BALANCE*/ select 1, now();
 ?column? |             now
----------+------------------------------
        1 | 2018-04-10 13:35:44.41823+01
(1 row)

Encontrará que state_change cambia al igual que el resultado.

Último punto aquí:¿por qué /*NO LOAD BALANCE*/ ?.. para asegurarnos de que verificamos pg_stat_activity en el maestro y también ejecutamos la consulta en el maestro. Lo mismo puede usar la sugerencia /*NO QUERY CACHE*/ para evitar obtener un resultado en caché.

¿Ya es mucho para una breve reseña? ¡Pero ni siquiera tocamos la parte HA! Y muchos usuarios miran hacia pgpool específicamente para esta función. Bueno, este no es el final de la historia, este es el final de la primera parte. Se acerca la segunda parte, donde cubriremos brevemente HA y algunos otros consejos sobre el uso de pgpool...