PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Eine Anleitung zu Pgpool für PostgreSQL:Teil Eins

Pgpool ist heute weniger aktuell als vor 10 Jahren, als es der Standardbestandteil eines Produktions-PostgreSQL-Setups war. Wenn jemand über PostgreSQL-Cluster sprach, bezog er sich oft auf postgreSQL hinter pgpool und nicht auf die PostgreSQL-Instanz selbst (was der richtige Begriff ist). Pgpool wird von den einflussreichsten Postgres-Spielern anerkannt:postgresql community, commandprompt, 2ndquadrant, EDB, citusdata, postgrespro (sortiert nach Alter, nicht nach Einfluss). Mir ist klar, dass der Bekanntheitsgrad meiner Links sehr unterschiedlich ist - ich möchte nur die Gesamtwirkung von pgpool in der Postgres-Welt betonen. Einige der bekanntesten aktuellen Postgres-„Anbieter“ wurden gefunden, nachdem pgpool bereits berühmt war. Was macht es also so berühmt?

Allein die Liste der am meisten nachgefragten angebotenen Funktionen lässt es großartig aussehen:

  • native Replikation
  • Verbindungspooling
  • Lastenausgleich für Leseskalierbarkeit
  • Hochverfügbarkeit (Watchdog mit virtueller IP, Online-Wiederherstellung und Failover)

Nun, lass uns einen Sandkasten bauen und spielen. Mein Beispiel-Setup ist der Master-Slave-Modus. Ich würde davon ausgehen, dass es heute am beliebtesten ist, da Sie normalerweise die Streaming-Replikation zusammen mit dem Lastenausgleich verwenden. Der Replikationsmodus wird heutzutage kaum noch verwendet. Die meisten DBAs überspringen es zugunsten von Streaming-Replikation und pglogical und früher zu slony.

Der Replikationsmodus hat viele interessante Einstellungen und sicherlich interessante Funktionen. Aber die meisten DBAs haben Master/Multi-Slave-Setup, wenn sie zu pgpool kommen. Sie suchen also nach automatischem Failover und Load Balancer, und pgpool bietet es sofort einsatzbereit für bestehende Master/Multi-Slave-Umgebungen an. Ganz zu schweigen davon, dass die Streaming-Replikation ab Postgres 9.4 ohne größere Fehler funktioniert und die Replikation ab 10 Hash-Indizes unterstützt wird, sodass Sie kaum etwas davon abhalten können, sie zu verwenden. Auch die Streaming-Replikation ist standardmäßig asynchron (konfigurierbar auf synchrone und sogar nicht „lineare“ Synchronisierung komplizierter Setups, während die native pgpool-Replikation synchron ist (was langsamere Datenänderungen bedeutet) ohne Wahlmöglichkeit. Es gelten auch zusätzliche Einschränkungen. Das Pgpool-Handbuch selbst schlägt vor, dies zu bevorzugen wenn möglich Streaming-Replikation über pgpool native). Und das ist hier meine Wahl.

Ah, aber zuerst müssen wir es installieren - richtig?

Installation (höherer Version auf Ubuntu).

Überprüfen Sie zuerst die Ubuntu-Version mit lsb_release -a. Repo ist für mich:

[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

Zuletzt die Installation selbst:

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

Konfig:

Ich verwende die Standardkonfiguration aus dem empfohlenen Modus:

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

Beginn:

Wenn Sie die Konfiguration verpasst haben, sehen Sie:

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

Ah stimmt - mein Fehler, aber leicht zu beheben (blind mit einem Liner machbar, wenn Sie den gleichen Benutzer für alle Gesundheitschecks und Wiederherstellungen wollen):

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

Und bevor wir fortfahren, erstellen wir die Datenbank pgpool und den Benutzer pgpool in allen Clustern (in meiner Sandbox sind sie Master, Failover und Slave, also muss ich sie nur auf Master ausführen):

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

Endlich - ab:

[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

Großartig - also können wir mit dem ersten Feature fortfahren - überprüfen wir den Lastenausgleich. Es hat einige Anforderungen, die verwendet werden müssen, unterstützt Hinweise (z. B. zum Ausgleich in derselben Sitzung), hat Funktionen auf der Schwarz-Weiß-Liste und eine auf regulären Ausdrücken basierende Umleitungspräferenzliste. Es ist anspruchsvoll. Leider würde es den Rahmen dieses Blogs sprengen, all diese Funktionen gründlich durchzugehen, daher werden wir die einfachsten Demos überprüfen:

Zunächst wird etwas sehr Einfaches zeigen, welcher Knoten für die Auswahl verwendet wird (in meinem Setup dreht Master auf 5400, Slave auf 5402 und Failover auf 5401, während pgpool selbst auf 5433 liegt, da ich einen anderen Cluster laufen habe und nicht stören wollte damit):

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

Dann in Schleife:

[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

Toll. Es gleicht die Last definitiv zwischen Knoten aus, scheint aber nicht gleichmäßig auszugleichen - vielleicht ist es so schlau, dass es das Gewicht jeder Anweisung kennt? Lassen Sie uns die Verteilung mit den erwarteten Ergebnissen überprüfen:

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)

Nein - pgpool analysiert nicht das Gewicht von Anweisungen - es war wieder ein DBA mit seinen Einstellungen! Die Einstellungen (siehe das Attribut lb_weight) stimmen mit den tatsächlichen Abfragezielzielen überein. Sie können es leicht ändern (wie wir es hier getan haben), indem Sie die entsprechende Einstellung ändern, zB:

[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
Laden Sie noch heute das Whitepaper PostgreSQL-Verwaltung und -Automatisierung mit ClusterControl herunterErfahren Sie, was Sie wissen müssen, um PostgreSQL bereitzustellen, zu überwachen, zu verwalten und zu skalierenLaden Sie das Whitepaper herunter

Toll! Die nächste große angebotene Funktion ist das Verbindungspooling. Mit 3.5 wird das „donnernde Herdenproblem“ durch die Serialisierung von Accept()-Aufrufen gelöst, wodurch die „Client-Verbindungs“-Zeit erheblich beschleunigt wird. Und doch ist diese Funktion ziemlich einfach. Es bietet nicht mehrere Ebenen von Pooling oder mehrere Pools, die für dieselbe Datenbank konfiguriert sind (pgpool lässt Sie jedoch auswählen, wo Selects mit database_redirect_preference_list des Lastausgleichs ausgeführt werden sollen), oder andere flexible Funktionen, die von pgBouncer angeboten werden.

Also kurze Demo:

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, stimmt, ich habe sie niedriger als die Standardeinstellung 32 geändert, sodass die Ausgabe nicht mehrere Seiten dauern würde. Nun, dann versuchen wir, die Anzahl der Sitzungen zu überschreiten (unten öffne ich Postgres-Sitzungen asynchron in einer Schleife, sodass die 6 Sitzungen mehr oder weniger zur gleichen Zeit angefordert werden):

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

Es lässt Sitzungen bis zu drei kommen - erwartet, da eine von der obigen Sitzung belegt wird (Auswahl aus pg_stat_activity), also 4-1=3. Sobald pg_sleep sein einsekündiges Nickerchen beendet und die Sitzung von postgres geschlossen wird, wird die nächste eingelassen. Nachdem die ersten drei beendet sind, treten die nächsten drei ein. Was passiert mit dem Rest? Sie werden in eine Warteschlange eingereiht, bis der nächste Verbindungsschlitz frei wird. Dann passiert der neben serialize_accept beschriebene Prozess und der Client wird verbunden.

Häh? Nur Sitzungspooling im Sitzungsmodus? Ist das alles?.. Nein, hier kommt das Caching ins Spiel! Siehe.:

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

Überprüfung der 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)

Führen Sie dann die erste Anweisung erneut aus und beobachten Sie, dass sich state_change nicht ändert, was bedeutet, dass Sie nicht einmal zur Datenbank gelangen, um ein bekanntes Ergebnis zu erhalten! Wenn Sie eine veränderliche Funktion einfügen, werden die Ergebnisse natürlich nicht zwischengespeichert. Experimentieren Sie mit:

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

Sie werden feststellen, dass sich state_change ebenso wie das Ergebnis ändert.

Letzter Punkt hier - warum /*NO LOAD BALANCE*/ ?.. um sicher zu gehen, überprüfen wir pg_stat_activity auf dem Master und führen auch eine Abfrage auf dem Master aus. Ebenso können Sie /*NO QUERY CACHE*/ Hinweis verwenden, um zu vermeiden, dass ein Ergebnis im Cache gespeichert wird.

Schon viel für einen kurzen Rückblick? Aber wir haben nicht einmal den HA-Teil berührt! Und viele Benutzer suchen speziell für diese Funktion nach pgpool. Nun, dies ist nicht das Ende der Geschichte, dies ist das Ende von Teil eins. Teil zwei kommt, wo wir HA und einige andere Tipps zur Verwendung von pgpool kurz behandeln werden...