Nejintuitivnějším způsobem upgradu databáze, který si dokážete představit, je vygenerovat repliku v nové verzi a provést v ní převzetí aplikace při selhání a v jiných enginech to funguje perfektně. S PostgreSQL to bývalo nativním způsobem nemožné. Chcete-li provést upgrady, museli jste přemýšlet o jiných způsobech upgradu, jako je použití pg_upgrade, dumping a obnovení nebo použití nástrojů třetích stran, jako je Slony nebo Bucardo, všechny mají svá vlastní upozornění. Je to kvůli způsobu, jakým PostgreSQL implementoval replikaci.
Streamovací replikace PostgreSQL (běžná replikace PostgreSQL) je fyzická replikace, která replikuje změny na úrovni bajtu po bajtu a vytváří identickou kopii databáze na jiném serveru. Tato metoda má mnoho omezení, když uvažujete o upgradu, protože jednoduše nemůžete vytvořit repliku v jiné verzi serveru nebo dokonce v jiné architektuře.
Od PostgreSQL 10 má implementovanou vestavěnou logickou replikaci, kterou na rozdíl od fyzické replikace můžete replikovat mezi různými hlavními verzemi PostgreSQL. To samozřejmě otevírá nové dveře pro upgradování strategií.
V tomto blogu uvidíme, jak můžete upgradovat PostgreSQL 11 na PostgreSQL 12 s nulovými prostoji pomocí logické replikace.
Logická replikace PostgreSQL
Logická replikace je metoda replikace datových objektů a jejich změn na základě jejich replikační identity (obvykle primární klíč). Je založen na režimu publikování a odběru, kde jeden nebo více odběratelů odebírá jednu nebo více publikací v uzlu vydavatele.
Publikace je sada změn generovaných z tabulky nebo skupiny tabulek (označovaná také jako replikační sada). Uzel, kde je definována publikace, se nazývá vydavatel. Předplatné je následnou stranou logické replikace. Uzel, kde je definováno předplatné, se označuje jako předplatitel a definuje připojení k jiné databázi a sadě publikací (jedné nebo více), ke kterým se chce přihlásit. Předplatitelé získávají data z publikací, které odebírají.
Logická replikace je postavena na architektuře podobné replikaci fyzického streamování. Je implementován procesy „walsender“ a „apply“. Proces walsender spustí logické dekódování WAL a načte standardní plugin pro logické dekódování. Plugin transformuje změny načtené z WAL do protokolu logické replikace a filtruje data podle specifikace publikace. Data jsou pak nepřetržitě přenášena pomocí streamingového replikačního protokolu k aplikačnímu pracovníkovi, který mapuje data do lokálních tabulek a aplikuje jednotlivé změny tak, jak jsou přijímány, ve správném transakčním pořadí.
Logická replikace začíná pořízením snímku dat v databázi vydavatelů a zkopíruje to předplatiteli. Počáteční data v existujících odebíraných tabulkách jsou zachycována a zkopírována v paralelní instanci speciálního druhu aplikačního procesu. Tento proces vytvoří svůj vlastní dočasný replikační slot a zkopíruje existující data. Jakmile jsou existující data zkopírována, pracovník přejde do režimu synchronizace, který zajistí, že tabulka bude uvedena do synchronizovaného stavu s hlavním procesem aplikace streamováním jakýchkoli změn, ke kterým došlo během počátečního kopírování dat, pomocí standardní logické replikace. Po dokončení synchronizace je řízení replikace tabulky předáno zpět hlavnímu procesu aplikace, kde replikace pokračuje jako obvykle. Změny ve vydavateli jsou odesílány odběrateli tak, jak k nim dochází v reálném čase.
Jak upgradovat PostgreSQL 11 na PostgreSQL 12 pomocí logické replikace
Chystáme se nakonfigurovat logickou replikaci mezi dvěma různými hlavními verzemi PostgreSQL (11 a 12) a samozřejmě poté, co toto zprovozníte, je pouze otázkou provedení převzetí služeb při selhání do databáze s novější verzí.
Provedeme následující kroky, abychom uvedli do provozu logickou replikaci:
- Nakonfigurujte uzel majitele stránek
- Nakonfigurujte uzel odběratele
- Vytvořte uživatele odběratele
- Vytvořte publikaci
- Vytvořte strukturu tabulky v odběrateli
- Vytvořte předplatné
- Zkontrolujte stav replikace
Takže začneme.
Na straně vydavatele budeme konfigurovat následující parametry v souboru postgresql.conf:
- adresy_poslechu: Na jaké IP adrese (adresách) poslouchat. Pro všechny budeme používat '*'.
- wal_level: Určuje, kolik informací se zapisuje do WAL. Nastavíme jej na „logický“.
- max_replication_slots :Určuje maximální počet replikačních slotů, které může server podporovat. Musí být nastaveno alespoň na počet předplatných, které se mají připojit, plus určitou rezervu pro synchronizaci tabulek.
- max_wal_senders: Určuje maximální počet souběžných připojení ze serverů v pohotovostním režimu nebo ze základních zálohovacích klientů streamování. Mělo by být nastaveno alespoň na stejnou hodnotu jako max_replication_slots plus počet fyzických replik, které jsou připojeny ve stejnou dobu.
Mějte na paměti, že některé z těchto parametrů vyžadovaly restart služby PostgreSQL.
Soubor pg_hba.conf je také potřeba upravit, aby umožňoval replikaci. Musíte povolit uživateli replikace připojení k databázi.
Na základě toho tedy nakonfigurujeme vydavatele (v tomto případě server PostgreSQL 11) následovně:
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
Musíte změnit uživatele (v tomto příkladu rep1), který bude použit pro replikaci, a IP adresu 10.10.10.131/32 pro IP, která odpovídá vašemu PostgreSQL 12 uzlu.
Na straně předplatitele také vyžaduje nastavení max_replication_slots. V tomto případě by měl být nastaven alespoň na počet předplatných, které budou přidány k předplatiteli.
Další parametry, které je zde také potřeba nastavit, jsou:
- max_logical_replication_workers :Určuje maximální počet pracovníků logické replikace. To zahrnuje jak pracovníky aplikací, tak pracovníky synchronizace tabulek. Pracovníci logické replikace jsou přebíráni z fondu definovaného pomocí max_worker_processes. Musí být nastaven alespoň na počet odběrů, opět plus nějaká rezerva na synchronizaci tabulky.
- max_worker_processes :Nastavuje maximální počet procesů na pozadí, které může systém podporovat. Možná bude nutné jej upravit, aby vyhovoval pracovníkům replikace, alespoň max_logical_replication_workers + 1. Tento parametr vyžaduje restart PostgreSQL.
Musíte tedy nakonfigurovat předplatitele (v tomto případě server PostgreSQL 12) následovně:
postgresql.conf:
listen_addresses = '*'
max_replication_slots = 4
max_logical_replication_workers = 4
max_worker_processes = 8
Vzhledem k tomu, že tento PostgreSQL 12 bude brzy novým primárním uzlem, měli byste v tomto kroku zvážit přidání parametrů wal_level a archive_mode, abyste se vyhnuli pozdějšímu restartu služby.
wal_level = logical
archive_mode = on
Tyto parametry budou užitečné, pokud chcete přidat novou repliku nebo pro použití záloh PITR.
Ve vydavateli musíte vytvořit uživatele, se kterým se odběratel spojí:
world=# CREATE ROLE rep1 WITH LOGIN PASSWORD '*****' REPLICATION;
CREATE ROLE
Role použitá pro připojení replikace musí mít atribut REPLICATION. Přístup pro roli musí být nakonfigurován v pg_hba.conf a musí mít atribut LOGIN.
Aby bylo možné zkopírovat počáteční data, musí mít role použitá pro replikační připojení oprávnění SELECT pro publikovanou tabulku.
world=# GRANT SELECT ON ALL TABLES IN SCHEMA public to rep1;
GRANT
Vytvoříme publikaci pub1 v uzlu vydavatele pro všechny tabulky:
world=# CREATE PUBLICATION pub1 FOR ALL TABLES;
CREATE PUBLICATION
Uživatel, který vytvoří publikaci, musí mít v databázi oprávnění CREATE, ale pro vytvoření publikace, která publikuje všechny tabulky automaticky, musí být uživatel superuživatel.
K potvrzení vytvořené publikace použijeme katalog pg_publication. Tento katalog obsahuje informace o všech publikacích vytvořených v databázi.
world=# SELECT * FROM pg_publication;
-[ RECORD 1 ]+-----
pubname | pub1
pubowner | 10
puballtables | t
pubinsert | t
pubupdate | t
pubdelete | t
pubtruncate | t
Popisy sloupců:
- název hospody :Název publikace.
- majitel hospody :Vlastník publikace.
- publikovatelné tabulky :Pokud je true, tato publikace automaticky zahrnuje všechny tabulky v databázi, včetně všech, které budou vytvořeny v budoucnu.
- pubinsert :Pokud je true, operace INSERT se replikují pro tabulky v publikaci.
- pubupdate :Pokud je true, operace UPDATE jsou replikovány pro tabulky v publikaci.
- pubdelete :Pokud je true, operace DELETE se replikují pro tabulky v publikaci.
- publikovat :Pokud je true, operace TRUNCATE jsou replikovány pro tabulky v publikaci.
Jelikož se schéma nereplikuje, musíte provést zálohu v PostgreSQL 11 a obnovit ji v PostgreSQL 12. Záloha bude provedena pouze pro schéma, protože informace budou replikovány v počátečním převod.
V PostgreSQL 11:
$ pg_dumpall -s > schema.sql
V PostgreSQL 12:
$ psql -d postgres -f schema.sql
Jakmile budete mít své schéma v PostgreSQL 12, musíte vytvořit předplatné a nahradit hodnoty hostitele, dbname, uživatele a hesla těmi, které odpovídají vašemu prostředí.
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
Výše uvedené zahájí proces replikace, který synchronizuje počáteční obsah tabulek tabulek v publikaci a poté zahájí replikaci přírůstkových změn do těchto tabulek.
Uživatel vytvářející předplatné musí být superuživatel. Proces použití předplatného bude spuštěn v místní databázi s oprávněními superuživatele.
K ověření vytvořeného předplatného můžete použít katalog pg_stat_subscription. Toto zobrazení bude obsahovat jeden řádek na předplatné pro hlavního pracovníka (s nulovým PID, pokud pracovník není spuštěn) a další řádky pro pracovníky zpracovávající počáteční kopii dat přihlášených tabulek.
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
Popisy sloupců:
- subid :OID předplatného.
- podnázev :Název předplatného.
- pid :ID procesu pracovního procesu předplatného.
- relid :OID vztahu, který pracovník synchronizuje; null pro hlavního aplikačního pracovníka.
- received_lsn :Bylo přijato místo posledního zápisu napřed, počáteční hodnota tohoto pole je 0.
- last_msg_send_time :Čas odeslání poslední zprávy přijaté od původního odesílatele WAL.
- last_msg_receipt_time :Čas přijetí poslední zprávy přijaté od původního odesílatele WAL.
- latest_end_lsn :Místo posledního zápisu napřed nahlášené původnímu odesílateli WAL.
- latest_end_time :Čas posledního umístění protokolu napřed nahlášené původnímu odesílateli WAL.
K ověření stavu replikace v primárním uzlu můžete použít 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
Popisy sloupců:
- pid :ID procesu procesu odesílatele WAL.
- usesysid :OID uživatele přihlášeného do tohoto procesu odesílatele WAL.
- usename :Jméno uživatele přihlášeného k tomuto procesu odesílatele WAL.
- název_aplikace :Název aplikace, která je připojena k tomuto odesílateli WAL.
- client_addr :IP adresa klienta připojeného k tomuto odesílateli WAL. Pokud je toto pole prázdné, znamená to, že klient je připojen prostřednictvím Unixového soketu na serveru.
- název_hostitele_klienta :Název hostitele připojeného klienta, jak je hlášeno zpětným vyhledáváním DNS adresy client_addr. Toto pole nebude mít hodnotu null pouze pro připojení IP a pouze v případě, že je povoleno log_hostname.
- client_port :Číslo TCP portu, který klient používá pro komunikaci s tímto odesílatelem WAL, nebo -1, pokud je použit soket Unix.
- backend_start :Čas, kdy byl tento proces zahájen.
- backend_xmin :Tento pohotovostní horizont xmin nahlášen hot_standby_feedback.
- stav :Aktuální stav odesílatele WAL. Možné hodnoty jsou:startup, catchup, streaming, backup and stopping.
- sent_lsn :Poslední umístění protokolu s předběžným zápisem odeslané při tomto připojení.
- write_lsn :Poslední umístění protokolu pro předběžný zápis zapsané na disk tímto pohotovostním serverem.
- flush_lsn :Poslední umístění protokolu napřed vyprázdněno na disk tímto pohotovostním serverem.
- replay_lsn :Umístění protokolu posledního zápisu napřed přehráno do databáze na tomto pohotovostním serveru.
- write_lag :Čas, který uplynul mezi místním vyprázdněním poslední WAL a přijetím oznámení, že jej tento pohotovostní server zapsal (ale ještě jej nevyprázdnil ani neaplikoval).
- flush_lag :Čas, který uplynul mezi místním vyprázdněním poslední WAL a přijetím oznámení, že jej tento pohotovostní server zapsal a vyprázdnil (ale ještě jej nepoužil).
- replay_lag :Čas, který uplynul mezi místním vyprázdněním posledního WAL a přijetím oznámení, že jej tento pohotovostní server zapsal, vyprázdnil a použil.
- priorita_synchronizace :Priorita tohoto rezervního serveru pro výběr jako synchronního pohotovostního režimu v synchronní replikaci založené na prioritách.
- sync_state :Synchronní stav tohoto pohotovostního serveru. Možné hodnoty jsou async, potential, sync, quorum.
Chcete-li ověřit, kdy je počáteční přenos dokončen, můžete zkontrolovat protokol PostgreSQL na předplatiteli:
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
Nebo zkontrolujte proměnnou srsubstate v katalogu pg_subscription_rel. Tento katalog obsahuje stav pro každý replikovaný vztah v každém předplatném.
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)
Popisy sloupců:
- srsubid :Odkaz na předplatné.
- srrelid :Odkaz na vztah.
- srsubstate :Stavový kód:i =inicializovat, d =data se kopírují, s =synchronizována, r =připraveno (normální replikace).
- srsublsn :Ukončete LSN pro stavy sar.
Můžete vložit nějaké testovací záznamy do PostgreSQL 11 a ověřit, že je máte v 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)
V tuto chvíli máte vše připraveno k nasměrování vaší aplikace na PostgreSQL 12.
Za tímto účelem musíte nejprve potvrdit, že nemáte zpoždění replikace.
Na primárním uzlu:
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
A nyní stačí změnit váš koncový bod z vaší aplikace nebo nástroje pro vyrovnávání zatížení (pokud jej máte) na nový server PostgreSQL 12.
Pokud máte nástroj pro vyrovnávání zatížení, jako je HAProxy, můžete jej nakonfigurovat pomocí PostgreSQL 11 jako aktivního a PostgreSQL 12 jako záložního tímto způsobem:
Pokud tedy právě vypnete starý primární uzel v PostgreSQL 11, záložní server, v tomto případě PostgreSQL 12, začne přijímat provoz transparentním způsobem pro uživatele/aplikaci.
Na konci migrace můžete smazat předplatné ve svém novém primárním uzlu v PostgreSQL 12:
world=# DROP SUBSCRIPTION sub1;
NOTICE: dropped replication slot "sub1" on publisher
DROP SUBSCRIPTION
A ověřte, zda je správně odstraněn:
world=# SELECT * FROM pg_subscription_rel;
(0 rows)
world=# SELECT * FROM pg_stat_subscription;
(0 rows)
Omezení
Před použitím logické replikace mějte na paměti následující omezení:
- Schéma databáze a příkazy DDL se nereplikují. Počáteční schéma lze zkopírovat pomocí pg_dump --schema-only.
- Sekvenční data se nereplikují. Data v sériových nebo identifikačních sloupcích podporovaných sekvencemi budou replikována jako součást tabulky, ale samotná sekvence bude stále zobrazovat počáteční hodnotu na odběrateli.
- Replikace příkazů TRUNCATE je podporována, ale při ořezávání skupin tabulek spojených cizími klíči je třeba věnovat určitou pozornost. Při replikaci akce zkrácení předplatitel zkrátí stejnou skupinu tabulek, která byla zkrácena na vydavateli, buď explicitně zadaná, nebo implicitně shromážděná prostřednictvím CASCADE, mínus tabulky, které nejsou součástí předplatného. To bude fungovat správně, pokud jsou všechny ovlivněné tabulky součástí stejného předplatného. Pokud však některé tabulky, které mají být zkráceny na odběrateli, mají odkazy s cizím klíčem na tabulky, které nejsou součástí stejného (nebo jakéhokoli) předplatného, pak aplikace akce zkrácení na odběratele selže.
- Velké objekty se nereplikují. Neexistuje žádné jiné řešení, než ukládání dat do normálních tabulek.
- Replikace je možná pouze ze základních tabulek na základní tabulky. To znamená, že tabulky v publikaci a na straně předplatného musí být normální tabulky, nikoli pohledy, materializované pohledy, kořenové tabulky oddílů nebo cizí tabulky. V případě diskových oddílů můžete replikovat hierarchii oddílů jedna ku jedné, ale aktuálně ji nelze replikovat do jinak rozděleného nastavení.
Závěr
Udržování vašeho PostgreSQL serveru v aktuálním stavu prováděním pravidelných upgradů bylo do verze PostgreSQL 10 nezbytným, ale obtížným úkolem. Naštěstí je to nyní jiný příběh díky logické replikaci.
V tomto blogu jsme krátce představili logickou replikaci, funkci PostgreSQL zavedenou nativně ve verzi 10, a ukázali jsme vám, jak vám může pomoci splnit tento upgrade z PostgreSQL 11 na PostgreSQL 12 se strategií nulových prostojů.