Pule połączeń to prosty, ale skuteczny sposób na poprawę wydajności aplikacji i zmniejszenie obciążenia serwerów PostgreSQL. Czytaj dalej, aby dowiedzieć się więcej o używaniu PgBouncera do łączenia połączeń PostgreSQL.
Dlaczego łączenie połączeń?
PostgreSQL ma dość ciężką architekturę obsługi połączeń. Dla każdego połączenia przychodzącego postmaster (główny demon Postgres) tworzy nowy proces (tradycyjnie nazywany backendem ) do obsługi. Chociaż ten projekt zapewnia lepszą stabilność i izolację, nie czyni go szczególnie wydajnym w obsłudze połączeń o krótkim czasie życia. Nowe połączenie klienta Postgres obejmuje konfigurację TCP, tworzenie procesów i inicjalizację zaplecza – wszystko to jest kosztowne pod względem czasu i zasobów systemowych.
To oczywiście tylko problem, jeśli połączenia są tworzone zbyt często i odrzucane bez ponownego użycia. Niestety, często zdarza się, że klaster węzłów sieciowych uruchamia aplikacje napisane w PHP lub innych tego typu językach, które muszą łączyć się z bazą danych raz na ładowanie strony. Powszechne są również zadania wsadowe, które szybko tworzą wiele połączeń w krótkich odstępach czasu. Stosuję połączenie połączeń w takich sytuacjach może drastycznie zmniejszyć obciążenie serwera PostgreSQL i znacznie poprawić opóźnienia zapytań.
Dzięki puli połączeń, klienci łączą się z serwerem proxy, który utrzymuje zestaw bezpośrednich połączeń z rzeczywistym serwerem PostgreSQL. Zazwyczaj klienci nie zdają sobie sprawy (i nie powinni) zdawać sobie sprawy, że są podłączeni do serwera proxy, a nie do rzeczywistego serwera. Proxy może działać na tym samym węźle co klient (na przykład na każdym węźle sieciowym), w którym to przypadku klienci mogą łączyć się z proxy przez gniazda domeny Unix, które mają bardzo niski narzut połączenia. Nawet jeśli serwer proxy znajduje się na innym węźle, a klient potrzebuje połączenia TCP, aby dotrzeć do serwera proxy, można uniknąć obciążenia nowego zaplecza Postgres.
Co to jest PgBouncer?
PgBouncer to open-source, lekki, jedno-binarny pooler połączeń dla PostgreSQL. Może łączyć połączenia z jedną lub większą liczbą baz danych (na prawdopodobnie różnych serwerach) i obsługiwać klientów przez gniazda domen TCP i Unix.
PgBouncer utrzymuje pulę połączeń dla każdego unikalnego użytkownika, pary bazy danych. Zwykle jest skonfigurowany do przekazywania jednego z tych połączeń nowemu połączeniu przychodzącemu i zwracania go z powrotem do puli, gdy klient się rozłączy. Możesz skonfigurować PgBouncer tak, aby pula była bardziej agresywna, aby mogła odbierać i zwracać połączenie do puli w granicach transakcji lub instrukcji, a nie granic połączenia. Istnieją jednak pewne potencjalnie niepożądane konsekwencje.
Powinieneś być w stanie zainstalować PgBouncer za pomocą menedżera pakietów swojej dystrybucji:
# RedHat/CentOS/..
$ sudo yum install pgbouncer
# Debian/Ubuntu/..
$ sudo apt-get install pgbouncer
Jest również dostępny w standardowych repozytoriach Postgres APT i YUM, których można użyć, jeśli pakiety twojej dystrybucji są stare lub uszkodzone.
PgBouncer opiera się na głównym pliku konfiguracyjnym, zwykle przechowywanym jako /etc/pgbouncer/pgbouncer.ini
. Możesz wywołać pgbouncer jako usługę systemd lub po prostu uruchomić go nawet bez uprawnień administratora ze ścieżką do tego pliku konfiguracyjnego.
Aby to zrobić, stwórzmy bazę danych db1 i użytkownika user1 na naszym serwerze:
$ 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=#
Klienci będą łączyć się z bazą danych db1
z nazwą użytkownika user1
andpassword user1pass
. Naszym celem jest, aby klienci połączyli się z PgBouncer, który będzie proxy i pulował połączenia z rzeczywistym serwerem.
Teraz utwórzmy plik (w dowolnym miejscu) z następującą zawartością:
[databases]
db1 = host=localhost dbname=db1
[pgbouncer]
listen_addr = 127.0.0.1
listen_port = 16432
auth_file = userlist.txt
Musimy również utworzyć plik „userlist.txt” w tym samym katalogu, z nazwą użytkownika i (zahaszowanymi) hasłami użytkowników, z którymi PgBouncer pozwoli się połączyć. Utwórz „userlist.txt” z następującą zawartością:
"user1" "md5638b81c77071ea624d1ad4adb1433540"
Druga wartość to MD5 „user1passuser1”, poprzedzone prefiksem „md5”. To jest zwykła konwencja Postgresa.
Teraz zacznijmy PgBouncera na pierwszym planie:
$ /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
Uruchomiliśmy teraz PgBouncer, który nasłuchuje na porcie TCP 127.0.0.1 16432, a także na gnieździe domeny uniksowej /tmp/.s.PGSQL.16432
. Jedyną „bazą danych” dostępną na tym serwerze proxy jest db1
. Jedynym użytkownikiem, który może połączyć się z tym serwerem jest user1
. Spróbujmy połączyć się z 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=>
Klient (psql) łączy się pomyślnie z hostem lokalnym:16432, ale widać, że połączenie jest w rzeczywistości przekazywane do hosta lokalnego:5432.
Możesz spróbować rozłączyć się i połączyć ponownie kilka razy, a następnie sprawdzić, ile połączeń jest nadal dostępnych na rzeczywistym serwerze:
postgres=# select count(*) from pg_stat_activity
postgres-# where datname='db1' and usename='user1';
count
-------
1
(1 row)
PgBouncer nie rozłącza rzeczywistego połączenia, gdy klient się rozłącza. Możesz skonfigurować minimalne, maksymalne i zarezerwowane połączenia, które PgBouncer będzie utrzymywał dla każdej puli w pliku konfiguracyjnym.
Wdrażanie PgBouncera
Gdzie instalujesz i uruchamiasz PgBouncer? Istnieją różne odpowiedzi, z różnymi zaletami:
- W węźle serwera Postgres :Możesz zainstalować go obok samego serwera PostgreSQL, na tym samym węźle. Klienci łączą się z portem PgBouncer, a nie portem Postgres. Ma to efekt „ulepszonego” Postgresa, który wewnętrznie tworzy pulę połączeń. Musisz także utrzymywać tylko jedną kopię plików konfiguracyjnych dla PgBouncer. Z drugiej strony wiąże się to z uruchomieniem czegoś innego również w węźle serwera PostgreSQL, co może nie być łatwe lub dozwolone (zapory ogniowe, polityki) lub nawet możliwe (AWSRDS).
- W węzłach klienta :Możesz zainstalować PgBouncer w każdym węźle klienta, na przykład każdy węzeł sieciowy uruchamia Apache i PHP, a skrypty PHP łączą się z localPgBouncer. Ma to tę zaletę, że nie trzeba zakłócać konfiguracji serwera, a konfigurację puli można wykorzystać do utrzymania przewidywalnego obciążenia serwera. Z drugiej strony, jeśli liczba węzłów klienckich jest ogromna lub może się znacznie różnić w zależności od obciążenia/ ruchu, serwer może być szybko przeciążony.
- Jako samodzielny klaster :Trzecia opcja, aby mieć klaster niezależnych, bezstanowych węzłów PgBouncer, na czele z systemem równoważenia obciążenia TCP, takim jak HAProxy. Ta konfiguracja, choć jest bardziej skomplikowana niż pozostałe dwie opcje, zapewnia maksymalną kontrolę i konfigurowalność.
Administracja
PgBouncer umożliwia użytkownikom oznaczonym jako administratorzy łączenie się z wirtualną bazą danych o nazwie „pgbouncer” i wydawanie poleceń w celu kontrolowania serwera i przeglądania statystyk. Aby to wypróbować, oznaczmy najpierw użytkownika „user1” jako administratora, modyfikując plik 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
Teraz użytkownik 1 może połączyć się z bazą danych o nazwie „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=#
Z tego miejsca możesz wykonywać różne czynności, takie jak włączanie lub wyłączanie określonej bazy danych, sprawdzanie i ponowne ładowanie konfiguracji i nie tylko:
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)
Monitorowanie
Istnieją również polecenia pokazujące różne statystyki dotyczące PgBouncera, w tym:
- Statystyki bazy danych dotyczące czasu trwania zapytania, czasu oczekiwania klienta, wykorzystania sieci, liczby transakcji
- Statystyki puli dotyczące liczby aktywnych i oczekujących klientów, bezczynnych i używanych połączeń z serwerem
Statystyki są pobierane za pomocą poleceń w stylu „SHOW xyz”, takich jak ta do pobierania statystyk dotyczących puli:
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
Dalsze czytanie
Strona główna PgBouncera zawiera więcej szczegółów na temat wszystkich różnych funkcji i opcji konfiguracyjnych PgBouncera.
- Strona główna PgBouncera
- Repozytorium GitHub PgBouncera
- Wiki Postgres zawiera informacje na temat puli połączeń
- Pgpool to kolejna opcja puli połączeń