PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

Un guide de Pgpool pour PostgreSQL :première partie

Pgpool est moins actuel aujourd'hui qu'il y a 10 ans, lorsqu'il était la partie par défaut d'une configuration PostgreSQL de production. Souvent, lorsque quelqu'un parlait de cluster PostgreSQL, il faisait référence à postgreSQL derrière pgpool et non à l'instance PostgreSQL elle-même (ce qui est le bon terme). Pgpool est reconnu parmi les acteurs Postgres les plus influents :communauté postgresql, commandprompt, 2ndquadrant, EDB, citusdata, postgrespro (classés par âge, pas par influence). Je me rends compte que le niveau de reconnaissance de mes liens est très différent - je veux juste souligner l'impact global de pgpool dans le monde postgres. Certains des "vendeurs" postgres actuels les plus connus ont été trouvés après que le pgpool soit déjà célèbre. Alors qu'est-ce qui le rend si célèbre ?

Rien que la liste des fonctionnalités proposées les plus demandées lui donne une belle apparence :

  • réplication native
  • regroupement de connexions
  • Équilibrage de charge pour l'évolutivité en lecture
  • haute disponibilité (chien de garde avec IP virtuelle, récupération en ligne et basculement)

Eh bien, faisons un bac à sable et jouons. Mon exemple de configuration est le mode maître-esclave. Je suppose que c'est le plus populaire aujourd'hui, car vous utilisez généralement la réplication en continu avec l'équilibrage de charge. Le mode de réplication est à peine utilisé de nos jours. La plupart des administrateurs de base de données l'ignorent en faveur de la réplication en continu et de pglogical, et auparavant de slony.

Le mode de réplication a de nombreux paramètres intéressants et des fonctionnalités sûrement intéressantes. Mais la plupart des DBA ont une configuration maître/multi-esclave au moment où ils accèdent à pgpool. Ils recherchent donc un basculement automatique et un équilibreur de charge, et pgpool le propose pour les environnements maître/multi-esclaves existants. Sans oublier qu'à partir de Postgres 9.4, la réplication en streaming fonctionne sans bogues majeurs et à partir de 10 index de hachage, la réplication est prise en charge, il n'y a donc pratiquement rien pour vous empêcher de l'utiliser. De plus, la réplication en continu est asynchrone par défaut (configurable sur des configurations compliquées de synchronisation synchrone et même non «linéaire», tandis que la réplication pgpool native est synchrone (ce qui signifie des changements de données plus lents) sans option de choix. Des limitations supplémentaires s'appliquent également. Le manuel de Pgpool lui-même suggère de préférer si possible la réplication en streaming sur pgpool natif). Et donc c'est mon choix ici.

Ah, mais nous devons d'abord l'installer - n'est-ce pas ?

Installation (de la version supérieure sur ubuntu).

Vérifiez d'abord la version d'ubuntu avec lsb_release -a. Pour moi, le dépôt est :

[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

Enfin l'installation proprement dite :

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

Configuration :

J'utilise la configuration par défaut du mode recommandé :

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

Début :

Si vous avez manqué la configuration, vous voyez :

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

Ah vrai - mon mauvais, mais facilement réparable (faisable à l'aveugle avec une seule doublure si vous voulez le même utilisateur pour tous les bilans de santé et la récupération) :

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

Et avant d'aller plus loin, créons la base de données pgpool et l'utilisateur pgpool dans tous les clusters (dans mon bac à sable, ils sont maître, basculement et esclave, donc je dois l'exécuter uniquement sur le maître) :

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

Enfin - démarrage :

[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

Super - nous pouvons donc passer à la première fonctionnalité - vérifions l'équilibrage de charge. Il a certaines exigences à utiliser, prend en charge les astuces (par exemple pour équilibrer dans la même session), a des fonctions en liste noire et blanche, a une liste de préférences de redirection basée sur des expressions régulières. C'est sophistiqué. Hélas, passer en revue toutes ces fonctionnalités serait hors de la portée de ce blog, nous allons donc vérifier les démos les plus simples :

Tout d'abord, quelque chose de très simple montrera quel nœud est utilisé pour la sélection (dans ma configuration, le maître tourne sur 5400, l'esclave sur 5402 et le basculement sur 5401, tandis que pgpool lui-même est sur 5433, car j'ai un autre cluster en cours d'exécution et je ne voulais pas interférer avec) :

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

Puis en boucle :

[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

Génial. Il équilibre définitivement la charge entre les nœuds, mais semble ne pas s'équilibrer de manière égale - peut-être est-il si intelligent qu'il connaît le poids de chaque instruction ? Vérifions la distribution avec les résultats attendus :

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)

Non - pgpool n'analyse pas le poids des déclarations - c'était encore un DBA avec ses paramètres ! Les paramètres (voir l'attribut lb_weight) correspondent aux cibles de destination réelles de la requête. Vous pouvez facilement le modifier (comme nous l'avons fait ici) en modifiant le paramètre correspondant, par exemple :

[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
Téléchargez le livre blanc aujourd'hui PostgreSQL Management &Automation with ClusterControlDécouvrez ce que vous devez savoir pour déployer, surveiller, gérer et faire évoluer PostgreSQLTélécharger le livre blanc

Génial! La prochaine grande fonctionnalité offerte est la mise en commun des connexions. Avec 3.5, le «problème du troupeau de tonnerre» est résolu en sérialisant les appels accept (), accélérant considérablement le temps de «connexion client». Et pourtant, cette fonctionnalité est assez simple. Il n'offre pas plusieurs niveaux de pooling ou plusieurs pools configurés pour la même base de données (pgpool vous permet de choisir où exécuter les sélections avec database_redirect_preference_list d'équilibrage de charge), ou d'autres fonctionnalités flexibles offertes par pgBouncer.

Bref démo :

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, c'est vrai, je les ai changés plus bas que le défaut 32, donc la sortie ne prendrait pas plusieurs pages. Bon alors, essayons de dépasser le nombre de sessions (ci-dessous j'ouvre des sessions postgres async en boucle, donc les 6 sessions seraient demandées plus ou moins en même temps) :

[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)

Il laisse les sessions venir par trois - prévu, car une est prise par la session ci-dessus (en sélectionnant à partir de pg_stat_activity) donc 4-1=3. Dès que pg_sleep termine sa seconde sieste et que la session est fermée par postgres, la suivante est autorisée. Ainsi, après les trois premières fins, les trois suivantes interviennent. Qu'arrive-t-il au reste ? Ils sont mis en file d'attente jusqu'à ce que le prochain créneau de connexion se libère. Ensuite, le processus décrit à côté de serialize_accept se produit et le client se connecte.

Hein? Simple regroupement de sessions en mode session ? Est-ce tout ?.. Non, ici la mise en cache intervient ! Regarde. :

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

Vérification de la pg_stat_activity :

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)

Ensuite, exécutez à nouveau la première instruction et observez que state_change ne change pas, ce qui signifie que vous n'accédez même pas à la base de données pour obtenir un résultat connu ! Bien sûr, si vous mettez une fonction mutable, les résultats ne seront pas mis en cache. Expérimentez avec :

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

Vous constaterez que state_change change tout comme le résultat.

Dernier point ici - pourquoi /*NO LOAD BALANCE*/ ?.. pour être sûr que nous vérifions pg_stat_activity sur le maître et exécutons également la requête sur le maître. De même, vous pouvez utiliser l'indice /*NO QUERY CACHE*/ pour éviter d'obtenir un résultat mis en cache.

Déjà beaucoup pour un petit bilan ? Mais nous n'avons même pas touché à la partie HA ! Et de nombreux utilisateurs se tournent vers pgpool spécifiquement pour cette fonctionnalité. Eh bien, ce n'est pas la fin de l'histoire, c'est la fin de la première partie. La deuxième partie arrive, où nous couvrirons brièvement HA et quelques autres conseils sur l'utilisation de pgpool...