Pgpool е по-малко актуален днес, отколкото преди 10 години, когато беше частта по подразбиране от настроена производствена PostgreSQL. Често, когато някой говореше за PostgreSQL клъстер, той има предвид postgreSQL зад pgpool, а не самия PostgreSQL екземпляр (което е правилният термин). Pgpool се разпознава между най-влиятелните играчи на Postgres:postgresql общност, commandprompt, 2ndquadrant, EDB, citusdata, postgrespro (подредени по възраст, а не по влияние). Осъзнавам, че нивото на разпознаване в моите връзки е много различно - просто искам да подчертая цялостното въздействие на pgpool в света на postgres. Някои от най-известните настоящи „доставчици“ на postgres бяха открити, след като pgpool вече беше известен. И така, какво го прави толкова известен?
Само списъкът с най-търсените предлагани функции го прави да изглежда страхотно:
- родна репликация
- обединяване на връзки
- балансиране на натоварването за мащабируемост на четене
- висока наличност (наблюдател с виртуален IP, онлайн възстановяване и отказ)
Е, нека направим пясъчник и да играем. Моята примерна настройка е главен подчинен режим. Предполагам, че е най-популярният днес, защото обикновено използвате стрийминг репликация заедно с балансиране на натоварването. Режимът на репликация почти не се използва в наши дни. Повечето администратори на база данни го пропускат в полза на стрийминг репликация и pglogical, а преди това на slony.
Режимът на репликация има много интересни настройки и със сигурност интересна функционалност. Но повечето DBA имат настройка главен/много подчинени, докато стигнат до pgpool. Така че те търсят автоматично отказване и балансиране на натоварването, а pgpool го предлага нестандартно за съществуващи главни/много подчинени среди. Да не говорим, че от Postgres 9.4, стрийминг репликацията работи без големи грешки и от 10 хеш индекса се поддържа репликация, така че едва ли има нещо, което да ви спре да го използвате. Също така стрийминг репликацията е асинхронна по подразбиране (конфигурира се за синхронни и дори не "линейни" сложни настройки за синхронизация, докато собствената репликация на pgpool е синхронна (което означава по-бавни промени на данните) без опция за избор. Прилагат се също допълнителни ограничения. Самото ръководство на Pgpool предлага да предпочитате когато е възможно стрийминг репликация през родната на pgpool). И това е моят избор тук.
А, но първо трябва да го инсталираме - нали?
Инсталация (на по-висока версия на ubuntu).
Първо проверете версията на ubuntu с lsb_release -a. За мен репо е:
[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
И накрая, самата инсталация:
sudo apt-get install pgpool2=3.7.2-1.pgdg16.04+1
Конфигурация:
Потребителска конфигурация по подразбиране от препоръчан режим:
zcat /usr/share/doc/pgpool2/examples/pgpool.conf.sample-stream.gz > /etc/pgpool2/pgpool.conf
Стартиране:
Ако сте пропуснали конфигурацията, виждате:
2018-03-22 13:52:53.284 GMT [13866] FATAL: role "nobody" does not exist
Ах, вярно - моята лоша, но лесно поправима (изпълнимо на сляпо с една линия, ако искате един и същ потребител за всички здравни проверки и възстановяване):
[email protected]:~# sed -i s/'nobody'/'pgpool'/g /etc/pgpool2/pgpool.conf
И преди да продължим по-нататък, нека създадем база данни pgpool и потребителски pgpool във всички клъстери (В моята пясъчна среда те са master, failover и slave, така че трябва да го стартирам само на master):
t=# create database pgpool;
CREATE DATABASE
t=# create user pgpool;
CREATE ROLE
Най-накрая - започване:
[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
Страхотно - така че можем да продължим към първата функция - нека проверим балансирането на натоварването. Той има някои изисквания за използване, поддържа съвети (например за балансиране в една и съща сесия), има черно-бели функции, има списък с предпочитания за пренасочване, базиран на регулярни изрази. Той е изискан. Уви, обстойното разглеждане на цялата тази функционалност би било извън обхвата на този блог, така че ще проверим най-простите демонстрации:
Първо, нещо много просто ще покаже кой възел се използва за избор (в моята настройка главен върти на 5400, подчинен на 5402 и отказ на 5401, докато самият pgpool е на 5433, тъй като имам друг клъстер, който работи и не исках да се намесвам с него):
[email protected]:~$ psql -h localhost -p 5433 t -c "select current_setting('port') from ts limit 1"
current_setting
-----------------
5400
(1 row)
След това в цикъл:
[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
Страхотен. Определено балансира натоварването между възлите, но изглежда не балансира еднакво – може би е толкова умен, че знае тежестта на всяко изявление? Нека проверим разпределението с очакваните резултати:
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)
Не - pgpool не анализира тежестта на изявленията - отново беше DBA с нейните настройки! Настройките (вижте атрибута lb_weight) се съгласуват с действителните цели на заявката. Можете лесно да го промените (както направихме тук), като промените съответната настройка, напр.:
[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
Изтеглете Бялата книга днес Управление и автоматизация на PostgreSQL с ClusterControl Научете какво трябва да знаете, за да внедрите, наблюдавате, управлявате и мащабирате PostgreSQLD Изтеглете Бялата книга Страхотен! Следващата страхотна предлагана функция е обединяването на връзки. С 3.5 „проблемът с гръмотевото стадо“ се решава чрез сериализиране на повикванията accept(), което значително ускорява времето за „свързване на клиента“. И все пак тази функция е доста ясна. Той не предлага няколко нива на обединяване или няколко пула, конфигурирани за една и съща база данни (pgpool ви позволява да изберете къде да стартирате selects с database_redirect_preference_list за балансиране на натоварването), или други гъвкави функции, предлагани от pgBouncer.
Толкова кратка демонстрация:
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)
А, вярно, промених ги по-ниско от 32 по подразбиране, така че изходът няма да отнеме няколко страници. Е, тогава, нека се опитаме да надвишим броя на сесиите (по-долу отварям postgres сесии, асинхронни в цикъл, така че 6-те сесии ще бъдат поискани повече или по-малко по едно и също време):
[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)
Позволява сесиите да идват с три - очаквано, тъй като една се приема от горната сесия (избиране от pg_stat_activity), така че 4-1=3. Веднага щом pg_sleep приключи своята една втора дрямка и сесията бъде затворена от postgres, следващата се пуска. Така че след приключването на първите три, следващите три стъпки влизат. Какво се случва с останалите? Те са на опашка, докато се освободи следващият слот за връзка. След това процесът, описан до serialize_accept, се случва и клиентът се свързва.
а? Само обединяване на сесии в режим на сесия? Всичко ли е?.. Не, тук влиза кеширането! Вижте.:
postgres=# /*NO LOAD BALANCE*/ select 1;
?column?
----------
1
(1 row)
Проверка на 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)
След това стартирайте отново първия израз и наблюдавайте, че state_change не се променя, което означава, че дори не стигате до базата данни, за да получите известен резултат! Разбира се, ако поставите някаква променлива функция, резултатите няма да се кешират. Експериментирайте с:
postgres=# /*NO LOAD BALANCE*/ select 1, now();
?column? | now
----------+------------------------------
1 | 2018-04-10 13:35:44.41823+01
(1 row)
Ще откриете, че state_change се променя, както и резултатът.
Последна точка тук - защо /*БЕЗ БАЛАНС НА LOAD BALANCE*/?.. за да сме сигурни, че проверяваме pg_stat_activity на главен и изпълняваме заявка и на главен. Същото можете да използвате /*NO QUERY CACHE*/ намек, за да избегнете получаването на кеширан резултат.
Вече много за кратък преглед? Но ние дори не докоснахме HA частта! И много потребители гледат към pgpool специално за тази функция. Е, това не е краят на историята, това е краят на първата част. Предстои част втора, където ще разгледаме накратко HA и някои други съвети за използването на pgpool...