Най-интуитивният начин за надграждане на базата данни, за който можете да се сетите, е да генерирате реплика в нова версия и да извършите отказ на приложението в нея и всъщност работи перфектно в други двигатели. С PostgreSQL това беше невъзможно по естествен начин. За да постигнете надстройки, трябва да помислите за други начини за надграждане, като например използване на pg_upgrade, изхвърляне и възстановяване или използване на някои инструменти на трети страни като Slony или Bucardo, като всички те имат свои собствени предупреждения. Това се дължи на начина, по който PostgreSQL използва репликацията.
Поточно репликацията на PostgreSQL (често срещаната репликация на PostgreSQL) е физическа репликация, която репликира промените на ниво байт по байт, създавайки идентично копие на базата данни в друг сървър. Този метод има много ограничения, когато мислите за надграждане, тъй като просто не можете да създадете реплика в различна версия на сървъра или дори в различна архитектура.
След PostgreSQL 10, той е внедрил вградена логическа репликация, която, за разлика от физическата репликация, можете да репликирате между различни основни версии на PostgreSQL. Това, разбира се, отваря нова врата за надграждане на стратегии.
В този блог ще видим как можете да надстроите своя PostgreSQL 11 до PostgreSQL 12 с нулев престой, като използвате логическа репликация.
Логическа репликация на PostgreSQL
Логическата репликация е метод за репликиране на обекти с данни и техните промени, въз основа на тяхната идентичност на репликация (обикновено първичен ключ). Базира се на режим на публикуване и абонамент, при който един или повече абонати се абонират за една или повече публикации на възел на издател.
Публикацията е набор от промени, генерирани от таблица или група таблици (наричани още набор за репликация). Възелът, където е дефинирана публикация, се нарича издател. Абонаментът е долната страна на логическата репликация. Възелът, където е дефиниран абонаментът, се нарича абонат и той дефинира връзката с друга база данни и набор от публикации (една или повече), за които иска да се абонира. Абонатите изтеглят данни от публикациите, за които се абонират.
Логическата репликация е изградена с архитектура, подобна на физическата поточно репликация. Реализира се чрез процеси "walsender" и "apply". Процесът на walsender стартира логическото декодиране на WAL и зарежда стандартния плъгин за логическо декодиране. Плъгинът преобразува промените, прочетени от WAL към протокола за логическа репликация и филтрира данните според спецификацията на публикацията. След това данните се прехвърлят непрекъснато с помощта на протокола за поточно репликация към работника за прилагане, който картографира данните в локални таблици и прилага отделните промени при получаването им в правилен транзакционен ред.
Логическата репликация започва с правене на моментна снимка на данните в базата данни на издателя и копиране на това на абоната. Първоначалните данни в съществуващите абонирани таблици се заснемат и копират в паралелен екземпляр на специален вид процес на прилагане. Този процес ще създаде свой собствен временен слот за репликация и ще копира съществуващите данни. След като съществуващите данни бъдат копирани, работникът влиза в режим на синхронизация, който гарантира, че таблицата е приведена в синхронизирано състояние с основния процес на прилагане чрез поточно предаване на всички промени, настъпили по време на първоначалното копиране на данни, като се използва стандартна логическа репликация. След като синхронизацията бъде извършена, контролът върху репликацията на таблицата се връща на основния процес на прилагане, където репликацията продължава както обикновено. Промените на издателя се изпращат на абоната, когато се извършват в реално време.
Как да надстроите PostgreSQL 11 до PostgreSQL 12 с помощта на логическа репликация
Ще конфигурираме логическа репликация между две различни основни версии на PostgreSQL (11 и 12) и разбира се, след като това работи, става въпрос само за извършване на отказ на приложение в база данни с по-нова версия.
Ще изпълним следните стъпки, за да задействаме логическата репликация:
- Конфигуриране на възела на издателя
- Конфигуриране на абонатния възел
- Създайте абонат потребител
- Създайте публикация
- Създайте структурата на таблицата в абоната
- Създайте абонамента
- Проверете състоянието на репликация
И така, нека започнем.
От страна на издателя ще конфигурираме следните параметри във файла postgresql.conf:
- адреси на_слушане: Какъв IP адрес(и) да слушате. Ще използваме '*' за всички.
- wal_level: Определя колко информация се записва в WAL. Ще го настроим на „логически“.
- max_replication_slots :Указва максималния брой слотове за репликация, които сървърът може да поддържа. Трябва да бъде настроен поне на броя абонаменти, които се очаква да се свържат, плюс известен резерв за синхронизиране на таблицата.
- max_wal_senders: Определя максималния брой едновременни връзки от сървъри в режим на готовност или поточно базови резервни клиенти. Трябва да бъде настроен поне на същото като max_replication_slots плюс броя на физическите реплики, които са свързани едновременно.
Имайте предвид, че някои от тези параметри изискват рестартиране на услугата PostgreSQL, за да се приложи.
Файлът pg_hba.conf също трябва да бъде коригиран, за да позволи репликация. Трябва да разрешите на потребителя за репликация да се свърже с базата данни.
Така че въз основа на това, нека конфигурираме издателя (в този случай сървъра на PostgreSQL 11) както следва:
postgresql.conf:
listen_addresses = '*'
wal_level = logical
max_wal_senders = 8
max_replication_slots = 4
pg_hba.conf:
# TYPE DATABASE USER ADDRESS METHOD
host all rep1 10.10.10.131/32 md5
Трябва да промените потребителя (в този пример rep1), който ще се използва за репликация, и IP адреса 10.10.10.131/32 за IP, който съответства на вашия PostgreSQL 12 възел.
От страната на абоната също така изисква да бъдат зададени max_replication_slots. В този случай трябва да бъде настроен поне на броя абонаменти, които ще бъдат добавени към абоната.
Другите параметри, които също трябва да бъдат зададени тук, са:
- max_logical_replication_workers :Указва максималния брой работници за логическа репликация. Това включва както работници за прилагане, така и работници за синхронизиране на таблици. Работниците за логическа репликация се вземат от пула, дефиниран от max_worker_processes. Трябва да бъде настроен поне на броя абонаменти, отново плюс малко резерв за синхронизиране на таблицата.
- max_worker_processes :Задава максималния брой фонови процеси, които системата може да поддържа. Може да се наложи да се коригира, за да отговаря на работниците за репликация, поне max_logical_replication_workers + 1. Този параметър изисква рестартиране на PostgreSQL.
И така, трябва да конфигурирате абоната (в този случай сървъра на PostgreSQL 12) както следва:
postgresql.conf:
listen_addresses = '*'
max_replication_slots = 4
max_logical_replication_workers = 4
max_worker_processes = 8
Тъй като този PostgreSQL 12 скоро ще бъде новият основен възел, трябва да помислите за добавяне на параметрите wal_level и archive_mode в тази стъпка, за да избегнете ново рестартиране на услугата по-късно.
wal_level = logical
archive_mode = on
Тези параметри ще бъдат полезни, ако искате да добавите нова реплика или за използване на PITR архиви.
В издателя трябва да създадете потребителя, с който абонатът ще се свърже:
world=# CREATE ROLE rep1 WITH LOGIN PASSWORD '*****' REPLICATION;
CREATE ROLE
Ролята, използвана за връзката за репликация, трябва да има атрибут REPLICATION. Достъпът за ролята трябва да бъде конфигуриран в pg_hba.conf и трябва да има атрибута LOGIN.
За да може да се копират първоначалните данни, ролята, използвана за връзката за репликация, трябва да има привилегията SELECT в публикувана таблица.
world=# GRANT SELECT ON ALL TABLES IN SCHEMA public to rep1;
GRANT
Ще създадем публикация pub1 в възела на издателя за всички таблици:
world=# CREATE PUBLICATION pub1 FOR ALL TABLES;
CREATE PUBLICATION
Потребителят, който ще създаде публикация, трябва да има привилегията CREATE в базата данни, но за да създаде публикация, която публикува всички таблици автоматично, потребителят трябва да е суперпотребител.
За да потвърдим създадената публикация, ще използваме каталога pg_publication. Този каталог съдържа информация за всички публикации, създадени в базата данни.
world=# SELECT * FROM pg_publication;
-[ RECORD 1 ]+-----
pubname | pub1
pubowner | 10
puballtables | t
pubinsert | t
pubupdate | t
pubdelete | t
pubtruncate | t
Описания на колони:
- име на публикация :Име на публикацията.
- pubowner :Собственик на публикацията.
- таблици за публикуване :Ако е вярно, тази публикация автоматично включва всички таблици в базата данни, включително всички, които ще бъдат създадени в бъдеще.
- pubinsert :Ако е вярно, операциите INSERT се репликират за таблици в публикацията.
- pubupdate :Ако е вярно, операциите UPDATE се репликират за таблици в публикацията.
- pubdelete :Ако е вярно, операциите DELETE се репликират за таблици в публикацията.
- pubtruncate :Ако е вярно, операциите TRUNCATE се репликират за таблици в публикацията.
Тъй като схемата не се репликира, трябва да направите резервно копие в PostgreSQL 11 и да го възстановите във вашия PostgreSQL 12. Архивът ще бъде взет само за схемата, тъй като информацията ще бъде репликирана в първоначалната прехвърляне.
В PostgreSQL 11:
$ pg_dumpall -s > schema.sql
В PostgreSQL 12:
$ psql -d postgres -f schema.sql
След като имате своята схема в PostgreSQL 12, трябва да създадете абонамента, като замените стойностите на хост, dbname, потребител и парола с тези, които съответстват на вашата среда.
PostgreSQL 12:
world=# CREATE SUBSCRIPTION sub1 CONNECTION 'host=10.10.10.130 dbname=world user=rep1 password=*****' PUBLICATION pub1;
NOTICE: created replication slot "sub1" on publisher
CREATE SUBSCRIPTION
Гореното ще стартира процеса на репликация, който синхронизира първоначалното съдържание на таблицата на таблиците в публикацията и след това започва да репликира постепенни промени в тези таблици.
Потребителят, който създава абонамент, трябва да е суперпотребител. Процесът на прилагане на абонамента ще се изпълнява в локалната база данни с привилегиите на суперпотребител.
За да проверите създадения абонамент, можете да използвате каталога pg_stat_subscription. Този изглед ще съдържа по един ред на абонамент за основния работник (с нулев PID, ако работникът не работи) и допълнителни редове за работници, обработващи първоначалното копие на данни на абонираните таблици.
world=# SELECT * FROM pg_stat_subscription;
-[ RECORD 1 ]---------+------------------------------
subid | 16422
subname | sub1
pid | 476
relid |
received_lsn | 0/1771668
last_msg_send_time | 2020-09-29 17:40:34.711411+00
last_msg_receipt_time | 2020-09-29 17:40:34.711533+00
latest_end_lsn | 0/1771668
latest_end_time | 2020-09-29 17:40:34.711411+00
Описания на колони:
- подза :OID на абонамента.
- подиме :Име на абонамента.
- pid :Идентификатор на процеса на работния процес на абонамента.
- relid :OID на връзката, която работникът синхронизира; null за основния работник за прилагане.
- received_lsn :Последното получено местоположение на дневника за предварителна запис, като първоначалната стойност на това поле е 0.
- last_msg_send_time :Изпращане на времето на последното съобщение, получено от източника WAL подател.
- last_msg_receipt_time :Време на получаване на последното съобщение, получено от източника на WAL подателя.
- latest_end_lsn :Последното местоположение на дневника с предварителна запис, съобщено на източника на WAL изпращача.
- най-късно_край_време :Час на последното местоположение на дневника с предварителна запис, съобщено на източника на WAL изпращача.
За да проверите състоянието на репликацията в основния възел, можете да използвате pg_stat_replication:
world=# SELECT * FROM pg_stat_replication;
-[ RECORD 1 ]----+------------------------------
pid | 527
usesysid | 16428
usename | rep1
application_name | sub1
client_addr | 10.10.10.131
client_hostname |
client_port | 35570
backend_start | 2020-09-29 17:40:04.404905+00
backend_xmin |
state | streaming
sent_lsn | 0/1771668
write_lsn | 0/1771668
flush_lsn | 0/1771668
replay_lsn | 0/1771668
write_lag |
flush_lag |
replay_lag |
sync_priority | 0
sync_state | async
Описания на колони:
- pid :Идентификатор на процес на процес на изпращач на WAL.
- usesysid :OID на потребителя, влязъл в този процес на изпращач на WAL.
- usename :Име на потребителя, влязъл в този процес на изпращане на WAL.
- име_на_приложение :Име на приложението, което е свързано с този подател на WAL.
- client_addr :IP адрес на клиента, свързан с този подател на WAL. Ако това поле е нула, това показва, че клиентът е свързан чрез Unix сокет на сървърната машина.
- име на_хост на клиента :Име на хост на свързания клиент, както се съобщава от обратно DNS търсене на client_addr. Това поле ще бъде ненулево само за IP връзки и само когато log_hostname е активирано.
- client_port :Номер на TCP порт, който клиентът използва за комуникация с този подател на WAL, или -1, ако се използва Unix сокет.
- backend_start :Времето, когато този процес е стартиран.
- backend_xmin :Xmin хоризонтът на този режим на готовност, докладван от hot_standby_feedback.
- състояние :Текущо състояние на изпращача на WAL. Възможните стойности са:startup, catchup, streaming, backup и stop.
- sent_lsn :Последното местоположение на дневника за предсрочно записване, изпратено на тази връзка.
- write_lsn :Последното местоположение на дневника за предварителна запис, записано на диск от този сървър в режим на готовност.
- flush_lsn :Последното местоположение на дневника за предварителна запис, прехвърлено на диск от този резервен сървър.
- replay_lsn :Последното местоположение на дневника за предсрочно записване, възпроизведено в базата данни на този резервен сървър.
- write_lag :Времето, изминало между локалното прочистване на скорошния WAL и получаването на известие, че този сървър в режим на готовност го е написал (но все още не го е прочистил или приложил).
- flush_lag :Време, изминало между локалното прочистване на скорошния WAL и получаването на известие, че този сървър в режим на готовност го е написал и прочистил (но все още не го е приложил).
- replay_lag :Времето, изминало между локалното прочистване на скорошния WAL и получаването на известие, че този сървър в режим на готовност го е написал, прочистил и приложил.
- sync_priority :Приоритет на този сървър в режим на готовност да бъде избран като синхронен режим на готовност в базирана на приоритет синхронна репликация.
- sync_state :Синхронно състояние на този сървър в режим на готовност. Възможните стойности са async, potencijal, sync, quorum.
За да проверите кога е завършен първоначалният трансфер, можете да проверите регистрационния файл на PostgreSQL на абоната:
2020-09-29 17:40:04.403 UTC [476] LOG: logical replication apply worker for subscription "sub1" has started
2020-09-29 17:40:04.411 UTC [477] LOG: logical replication table synchronization worker for subscription "sub1", table "city" has started
2020-09-29 17:40:04.422 UTC [478] LOG: logical replication table synchronization worker for subscription "sub1", table "country" has started
2020-09-29 17:40:04.516 UTC [477] LOG: logical replication table synchronization worker for subscription "sub1", table "city" has finished
2020-09-29 17:40:04.522 UTC [479] LOG: logical replication table synchronization worker for subscription "sub1", table "countrylanguage" has started
2020-09-29 17:40:04.570 UTC [478] LOG: logical replication table synchronization worker for subscription "sub1", table "country" has finished
2020-09-29 17:40:04.676 UTC [479] LOG: logical replication table synchronization worker for subscription "sub1", table "countrylanguage" has finished
Или проверка на променливата srsubstate в каталога pg_subscription_rel. Този каталог съдържа състоянието за всяка репликирана връзка във всеки абонамент.
world=# SELECT * FROM pg_subscription_rel;
srsubid | srrelid | srsubstate | srsublsn
---------+---------+------------+-----------
16422 | 16386 | r | 0/1771630
16422 | 16392 | r | 0/1771630
16422 | 16399 | r | 0/1771668
(3 rows)
Описания на колони:
- srsubid :Препратка към абонамента.
- srrelid :Препратка към релация.
- srsubstate :Код на състоянието:i =инициализира се, d =данните се копират, s =синхронизирани, r =готов (нормална репликация).
- srsublsn :Край на LSN за s и r състояния.
Можете да вмъкнете някои тестови записи във вашия PostgreSQL 11 и да потвърдите, че ги имате във вашия PostgreSQL 12:
PostgreSQL 11:
world=# INSERT INTO city (id,name,countrycode,district,population) VALUES (5001,'city1','USA','District1',10000);
INSERT 0 1
world=# INSERT INTO city (id,name,countrycode,district,population) VALUES (5002,'city2','ITA','District2',20000);
INSERT 0 1
world=# INSERT INTO city (id,name,countrycode,district,population) VALUES (5003,'city3','CHN','District3',30000);
INSERT 0 1
PostgreSQL 12:
world=# SELECT * FROM city WHERE id>5000;
id | name | countrycode | district | population
------+-------+-------------+-----------+------------
5001 | city1 | USA | District1 | 10000
5002 | city2 | ITA | District2 | 20000
5003 | city3 | CHN | District3 | 30000
(3 rows)
В този момент имате всичко готово, за да насочите приложението си към вашия PostgreSQL 12.
За това, първо, трябва да потвърдите, че нямате забавяне на репликацията.
На основния възел:
world=# SELECT application_name, pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn) lag FROM pg_stat_replication;
-[ RECORD 1 ]----+-----
application_name | sub1
lag | 0
И сега трябва само да промените вашата крайна точка от вашето приложение или балансьор на натоварване (ако имате такъв) към новия сървър на PostgreSQL 12.
Ако имате балансьор на натоварване като HAProxy, можете да го конфигурирате, като използвате PostgreSQL 11 като активен и PostgreSQL 12 като резервно копие, по този начин:
И така, ако просто изключите стария първичен възел в PostgreSQL 11, резервният сървър, в този случай в PostgreSQL 12, започва да получава трафика по прозрачен начин за потребителя/приложението.
В края на миграцията можете да изтриете абонамента в новия си основен възел в PostgreSQL 12:
world=# DROP SUBSCRIPTION sub1;
NOTICE: dropped replication slot "sub1" on publisher
DROP SUBSCRIPTION
И проверете дали е премахнат правилно:
world=# SELECT * FROM pg_subscription_rel;
(0 rows)
world=# SELECT * FROM pg_stat_subscription;
(0 rows)
Ограничения
Преди да използвате логическата репликация, моля, имайте предвид следните ограничения:
- Схемата на базата данни и DDL командите не се репликират. Първоначалната схема може да бъде копирана с помощта на pg_dump --schema-only.
- Данните за последователността не се репликират. Данните в колони за последователност или идентичност, подкрепени от последователности, ще бъдат репликирани като част от таблицата, но самата последователност все още ще показва началната стойност на абоната.
- Поддържа се репликация на команди TRUNCATE, но трябва да се внимава при съкращаването на групи от таблици, свързани с външни ключове. Когато репликира действие за съкращаване, абонатът ще съкрати същата група от таблици, която е била съкратена на издателя, или изрично посочена, или имплицитно събрана чрез CASCADE, минус таблици, които не са част от абонамента. Това ще работи правилно, ако всички засегнати таблици са част от един и същ абонамент. Но ако някои таблици, които трябва да бъдат съкратени на абоната, имат връзки с външен ключ към таблици, които не са част от същия (или какъвто и да е) абонамент, тогава прилагането на действието за съкращаване към абоната няма да бъде успешно.
- Големи обекти не се репликират. Няма заобиколно решение за това, освен съхраняване на данни в нормални таблици.
- Репликацията е възможна само от базови таблици към базови таблици. Тоест таблиците в публикацията и от страната на абонамента трябва да бъдат нормални таблици, а не изгледи, материализирани изгледи, основни таблици на дялове или чужди таблици. В случай на дялове, можете да репликирате йерархия на дялове едно към едно, но понастоящем не можете да репликирате към различно разделена настройка.
Заключение
Поддържането на вашия PostgreSQL сървър актуален чрез извършване на редовни надстройки беше необходима, но трудна задача до версията на PostgreSQL 10. За щастие сега това е различна история благодарение на логическата репликация.
В този блог направихме кратко въведение в логическата репликация, функция на PostgreSQL, въведена първоначално във версия 10, и ние ви показахме как може да ви помогне да постигнете това предизвикателство от PostgreSQL 11 до PostgreSQL 12 със стратегия за нулев престой.