W tym wpisie na blogu omówimy replikację logiczną w PostgreSQL:jej przypadki użycia, ogólne informacje o stanie tej technologii oraz specjalny przypadek użycia, w szczególności o tym, jak skonfigurować węzeł subskrybenta (replikę) serwera podstawowego w kolejności funkcjonować jako serwer bazy danych dla środowiska testowego i napotkane wyzwania.
Wprowadzenie
Replikacja logiczna, oficjalnie wprowadzona w PostgreSQL 10, to najnowsza technologia replikacji oferowana przez społeczność PostgreSQL. Replikacja logiczna jest kontynuacją spuścizny replikacji fizycznej, z którą dzieli wiele pomysłów i kodu. Replikacja logiczna działa jak replikacja fizyczna przy użyciu WAL do rejestrowania zmian logicznych niezależnie od wersji lub określonej architektury. Aby móc zapewnić logiczną replikację do podstawowej oferty, społeczność PostgreSQL przeszła długą drogę.
Rodzaje replikacji i historia replikacji PostgreSQL
Rodzaje replikacji w bazach danych można sklasyfikować w następujący sposób:
- Fizyczna (binarna) replikacja
- Poziom systemu operacyjnego (replikacja vSphere)
- Poziom systemu plików (DRBD)
- Poziom bazy danych (na podstawie WAL)
- Replikacja logiczna (poziom bazy danych)
- Oparty na wyzwalaczu (DBMirror, Slony)
- Oprogramowanie pośrednie (pgpool)
- Oparty na WAL (pglogiczna, logiczna replikacja)
Mapa drogowa, która wprowadza do dzisiejszej replikacji logicznej opartej na WAL, była następująca:
- 2001:DBMirror (w oparciu o wyzwalacz)
- 2004:Slony1 (oparty na wyzwalaczu), pgpool (oprogramowanie pośrednie)
- 2005:PITR (oparty na WAL) wprowadzony w PostgreSQL 8.0
- 2006:Ciepłe czuwanie w PostgreSQL 8.2
- 2010:Fizyczna replikacja strumieniowa, gorący stan gotowości w PostgreSQL 9.0
- 2011:Synchroniczna replikacja strumieniowa w PostgreSQL 9.1
- 2012:Kaskadowa replikacja strumieniowa w PostgreSQL 9.2
- 2013:Roboty działające w tle w PostgreSQL 9.3
- 2014:Interfejs API do dekodowania logicznego, gniazda replikacji. (Podstawy replikacji logicznej) w PostgreSQL 9.4
- 2015:2nd Quadrant wprowadza pglogikę, przodka lub replikację logiczną
- 2017:Logiczna replikacja w rdzeniu PostgreSQL 10!
Jak widzimy, współpracowało wiele technologii, aby urzeczywistnić replikację logiczną:archiwizacja WAL, gorące/gorące czuwania, fizyczna replikacja WAL, pracownicy w tle, dekodowanie logiczne. Zakładając, że czytelnik jest zaznajomiony z większością pojęć replikacji fizycznej, omówimy podstawowe elementy replikacji logicznej.
Podstawowe koncepcje replikacji logicznej PostgreSQL
Trochę terminologii:
- Publikacja: Zestaw zmian ze zbioru tabel zdefiniowanych w określonej bazie danych na głównym serwerze replikacji fizycznej. Publikacja może obsługiwać wszystkie lub niektóre z:INSERT, DELETE, UPDATE, OBCIĄŻANIE.
- Węzeł wydawcy: Serwer, na którym znajduje się publikacja.
- Tożsamość repliki: Sposób na zidentyfikowanie wiersza po stronie subskrybenta dla aktualizacji i usunięcia.
- Subskrypcja: Połączenie z węzłem wydawcy i co najmniej jedną publikacją w nim. Subskrypcja korzysta z dedykowanego miejsca replikacji w wydawcy do replikacji. Dodatkowe sloty replikacji mogą być użyte na początkowym etapie synchronizacji.
- Węzeł subskrybenta: Serwer, na którym znajduje się subskrypcja.
Replikacja logiczna jest zgodna z modelem publikowania/subskrypcji. Jeden lub więcej subskrybentów może subskrybować jedną lub więcej publikacji w węźle wydawcy. Subskrybenci mogą ponownie publikować, aby umożliwić replikację kaskadową. Logiczna replikacja tabeli składa się z dwóch etapów:
- Zrobienie zrzutu tabeli na wydawcy i skopiowanie go do subskrybenta
- Zastosowanie wszystkich zmian (od zrzutu) w tej samej kolejności
Replikacja logiczna ma charakter transakcyjny i gwarantuje, że kolejność zmian wprowadzanych do subskrybenta pozostaje taka sama jak w przypadku wydawcy. Replikacja logiczna daje znacznie więcej swobody niż replikacja fizyczna (binarna), dlatego może być używana na więcej sposobów:
- Replikacja pojedynczej bazy danych lub tabeli (nie ma potrzeby replikowania całego klastra)
- Ustawianie wyzwalaczy dla subskrybenta dla określonego zadania (takiego jak anonimizacja, co jest dość gorącym tematem po wejściu w życie RODO)
- Posiadanie węzła subskrybenta zbierającego dane z wielu węzłów wydawców, co pozwala na centralne przetwarzanie analityczne
- Replikacja między różnymi wersjami/architekturami/platformami (aktualizacje bez przestojów)
- Używanie węzła subskrybenta jako serwera bazy danych w środowisku testowym/rozwojowym. Chcemy tego, ponieważ testowanie na rzeczywistych danych jest najbardziej realistycznym rodzajem testów.
Ostrzeżenia i ograniczenia
Są pewne rzeczy, o których musimy pamiętać podczas korzystania z replikacji logicznej, niektóre z nich mogą wpływać na niektóre decyzje projektowe, ale inne mogą prowadzić do krytycznych incydentów.
Ograniczenia
- Obsługiwane są tylko operacje DML. Brak DDL. Schemat musi być wcześniej zdefiniowany
- Sekwencje nie są replikowane
- Duże obiekty nie są replikowane
- Obsługiwane są tylko zwykłe tabele podstawowe (widoki zmaterializowane, tabele główne partycji, tabele obce nie są obsługiwane)
Ostrzeżenia
Podstawowym problemem, z którym prędzej czy później będziemy musieli się zmierzyć podczas korzystania z replikacji logicznej, są konflikty po stronie subskrybenta. Subskrybent jest normalnym serwerem odczytu/zapisu, który może działać jako podstawowy w fizycznej konfiguracji replikacji, a nawet jako wydawca w kaskadowej logicznej konfiguracji replikacji. Dopóki zapisy na subskrybowanych tabelach są wykonywane, mogą wystąpić konflikty . Konflikt powstaje, gdy zreplikowane dane naruszają ograniczenie w tabeli, do której są stosowane. Zwykle operacja, która to powoduje, to INSERT, DELETE lub UPDATES, która nie ma żadnego efektu z powodu brakujących wierszy, nie spowoduje konfliktu. Kiedy pojawia się konflikt, replikacja zatrzymuje się. Logiczny proces roboczy w tle zostanie ponownie uruchomiony w określonym przedziale czasu (wal_retrieve_retry_interval), jednak replikacja nie powiedzie się ponownie, dopóki przyczyna konfliktu nie zostanie rozwiązana. Jest to stan krytyczny, z którym należy się natychmiast uporać. Jeśli tego nie zrobisz, miejsce replikacji utknie w swojej obecnej pozycji węzeł wydawcy zacznie gromadzić WAL i nieuchronnie w węźle wydawcy zabraknie miejsca na dysku . Konflikt jest najczęstszą przyczyną zatrzymania replikacji, ale każdy inny błędny warunek będzie miał ten sam skutek:np. dodaliśmy nową kolumnę NOT NULL do subskrybowanej tabeli, ale zapomnieliśmy zdefiniować wartość domyślną lub dodaliśmy kolumnę do opublikowanej tabeli, ale zapomnieliśmy ją zdefiniować w subskrybowanej tabeli, lub popełniliśmy błąd co do jej typu i oba typy nie są zgodny. Wszystkie te błędy zatrzymają replikację. Istnieją dwa sposoby rozwiązania konfliktu:
- Rozwiąż rzeczywisty problem
- Pomiń nieudaną transakcję, wywołując pg_replication_origin_advance
Rozwiązanie b. jak również tutaj pokazano, może być niebezpieczne i trudne, ponieważ jest to w zasadzie proces prób i błędów, a jeśli ktoś wybierze bieżący LSN na wydawcy, może łatwo skończyć z uszkodzonym systemem replikacji, ponieważ mogą wystąpić operacje między problematycznym LSN i bieżący numer LSN, który chcielibyśmy zachować. Najlepszym więc sposobem jest faktyczne rozwiązanie problemu po stronie abonenta. Np. jeśli dojdzie do naruszenia UNIQUE KEY, możemy zaktualizować dane subskrybenta lub po prostu usunąć wiersz. W środowisku produkcyjnym wszystko to musi być zautomatyzowane lub przynajmniej częściowo zautomatyzowane.
Konfigurowanie węzłów wydawcy i subskrybenta
Aby zapoznać się z ogólnym przeglądem replikacji logicznej w praktyce, przeczytaj ten blog.
Odpowiednie parametry replikacji logicznej to:
- Strona wydawcy
- wal_level>=„logiczny”
- max_replication_slots>=#subskrypcje + początkowa synchronizacja tabeli
- max_wal_senders>=max_replication_slots + inne_fizyczne_standby
- Strona subskrybenta
- max_replication_slots>=#subskrypcje
- max_logical_replication_workers>=#subskrypcje + początkowa synchronizacja tabeli
- max_worker_processes>=max_logical_replication_workers + 1 + max_parallel_workers
Skoncentrujemy się na szczególnych rozważaniach, które wynikają z naszego specjalnego celu, do osiągnięcia którego potrzebujemy logicznej replikacji:utwórz klaster testowej bazy danych do użytku przez dział testowania . Publikacja może być zdefiniowana dla wszystkich tabel lub tabela po tabeli. Proponuję podejście tabela po stole, ponieważ daje nam to maksymalną elastyczność. Ogólne kroki można podsumować w następujący sposób:
- Wykonaj nową initdb na węźle subskrybenta
- Zrzuć schemat klastra wydawcy i skopiuj go do węzła subskrybenta
- Utwórz schemat dla subskrybenta
- Zdecyduj, których stołów potrzebujesz, a których nie.
Jeśli chodzi o powyższy punkt, istnieją dwa powody, dla których replikacja lub konfiguracja replikacji może nie być potrzebna:
- Jest to atrapa stołu bez znaczenia (i może powinieneś usunąć go również z produkcji)
- jest tabelą lokalną w środowisku produkcyjnym, co oznacza, że ma sens, że ta sama tabela w środowisku testowym (subskrybenta) ma swoje własne dane
Wszystkie tabele biorące udział w replikacji logicznej muszą mieć TOŻSAMOŚĆ REPLIKI. Domyślnie jest to KLUCZ PODSTAWOWY, a jeśli nie jest dostępny, można zdefiniować klucz UNIKALNY. Następnym krokiem jest znalezienie stanu tabel w odniesieniu do TOŻSAMOŚCI REPLIK.
- Znajdź tabele bez oczywistego kandydata na IDENTYFIKACJA REPLIKI
select table_schema||'.'||table_name from information_schema.tables where table_type='BASE TABLE' AND table_schema||'.'||table_name NOT IN (select table_schema||'.'||table_name from information_schema.table_constraints WHERE constraint_type in ('PRIMARY KEY','UNIQUE')) AND table_schema NOT IN ('information_schema','pg_catalog') ;
- Znajdź tabele bez KLUCZA PODSTAWOWEGO, ale z UNIKATOWYM INDEKSEM
select table_schema||'.'||table_name from information_schema.table_constraints WHERE constraint_type = 'UNIQUE' EXCEPT select table_schema||'.'||table_name from information_schema.table_constraints WHERE constraint_type = 'PRIMARY KEY';
- Przejrzyj powyższe listy i zdecyduj, co zrobić z każdą tabelą
- Utwórz publikację z tabelami, dla których istnieje PK
select 'CREATE PUBLICATION data_for_testdb_pub FOR TABLE ONLY ' || string_agg(qry.tblname,', ONLY ') FROM (select table_schema||'.'||quote_ident(table_name) as tblname from information_schema.tables where table_type='BASE TABLE' AND table_schema||'.'||table_name IN (select table_schema||'.'||table_name from information_schema.table_constraints WHERE constraint_type in ('PRIMARY KEY')) AND table_schema NOT IN( 'information_schema','pg_catalog') ORDER BY 1) as qry; \gexec
- Następnie utwórz subskrypcję w węźle subskrybenta
Powyższe skopiuje również dane.create subscription data_for_testdb_pub CONNECTION 'dbname=yourdb host=yourdbhost user=repmgr' PUBLICATION data_for_testdb_pub ;
- Dodaj żądane tabele, które mają UNIKALNY indeks
Uruchom zarówno w węzłach wydawcy, jak i subskrybenta, np.:
Wydawca:ALTER TABLE someschema.yourtable REPLICA IDENTITY USING INDEX yourindex_ukey;
Na subskrybenta:ALTER PUBLICATION data_for_testdb_pub ADD TABLE ONLY someschema.yourtable;
ALTER SUBSCRIPTION data_for_testdb_pub REFRESH PUBLICATION WITH ( COPY_DATA );
- W tym momencie (synchronizacja) zawsze powinieneś mieć oko na log PostgreSQL w węźle subskrybenta. Nie chcesz żadnych błędów ani niczego (przekroczenia limitu czasu), które uniemożliwiają kontynuację replikacji logicznej. NATYCHMIAST ROZWIĄZUJ WSZELKIE BŁĘDY lub wydawca będzie gromadził pliki WAL w pg_wal i ostatecznie zabraknie miejsca. Musisz więc poradzić sobie z
- Wszystkie BŁĘDY lub wszelkie komunikaty dotyczące pracownika logicznego, które powodują zamknięcie
- Zadbaj również o
- wal_receiver_timeout
- wal_sender_timeout
Po rozwiązaniu wszystkich problemów Twój węzeł abonencki powinien działać szczęśliwie. Więc następnym pytaniem jest, jak używać tego jako serwera testowej bazy danych. Będziesz musiał poradzić sobie z tymi problemami/problemami:
- Anonimizacja
- Klucze podstawowe i klucze unikatowe oparte na naruszeniach sekwencji
- Ogólny zestaw dobrych praktyk
- Monitorowanie
Anonimizacja
Jeśli chodzi o anonimizację danych osobowych, która jest egzekwowana przez RODO w UE, należy napisać kilka wyzwalaczy ZAWSZE, które wygasają wszystkie pola dotyczące adresów, kont bankowych, stanu cywilnego, numerów telefonów, e-maili itp. Należy skonsultować się z pracownikiem ochrony w firmie w sprawie co zachować, a co wymazać. Wyzwalacze powinny być zdefiniowane jako ZAWSZE, ponieważ pracownik logiczny uruchamia instrukcje jako REPLIKĘ.
Klucze podstawowe z sekwencjami
Jeśli chodzi o sekwencje, wyraźnie będzie problem z tymi klawiszami, chyba że zostanie to rozwiązane przed rozpoczęciem jakiegokolwiek testu. Rozważ ten przypadek:
- W piątek po południu przeprowadzasz testy bazy danych subskrybentów wstawiając nowy wiersz do jakiejś tabeli. Będzie to miało jako identyfikator następną wartość wygenerowaną przez sekwencję.
- Idziesz do domu na weekend.
- Niektórzy użytkownicy produkcyjni wpisują wiersz w tej samej tabeli w bazie danych wydawcy.
- Wiersz zostanie zreplikowany na podstawie TOŻSAMOŚCI REPLIKI do węzła abonenckiego, ale zakończy się niepowodzeniem z powodu błędu naruszenia PK. Logiczny proces roboczy w tle zakończy działanie i spróbuje ponownie. Ale będzie zawodzić, dopóki problem będzie się utrzymywał.
- Replikacja zatrzyma się. Gniazdo replikacji zacznie gromadzić WAL.
- Wydawcy zabrakło miejsca na dysku.
- W weekend otrzymasz wiadomość e-mail, że Twój węzeł główny wpadł w panikę!
Tak więc, aby rozwiązać problem z sekwencją, możesz zastosować następujące podejście:
select 'SELECT setval(''' || seqrelid::regclass||''','||CASE WHEN seqincrement <0 THEN -214748364 ELSE 214748364 END||');' from pg_sequence where seqtypid=20;
\gexec
To, co robi powyższe, to ustawienie sekwencji na wystarczająco dużą wartość, aby nigdy nie nakładały się na siebie przez dość duże okno w przyszłości, co pozwala na bezproblemowy serwer testowy.
Zestaw dobrych praktyk
Naprawdę powinieneś powiedzieć swoim programistom, aby ich testy nie były trwałe. Dlatego każdy test po jego zakończeniu powinien pozostawić bazę danych w tym samym stanie, w jakim była przed testem. W przypadku wstawiania identyfikatorów opartych na sekwencji nie stanowi to problemu, widzieliśmy wcześniej rozwiązanie. Ale z niesekwencyjnymi (np. złożonymi) kluczami UNIQUE, które mogą być problemem. Dlatego najlepiej jest usunąć te dane testowe, zanim jakiś wiersz produkcyjny o tej samej wartości trafi do subskrybowanej tabeli.
Tutaj powinniśmy również dodać obsługę zmian w schemacie. Wszystkie zmiany schematu muszą być wykonane również na subskrybencie, aby nie przerywać replikowanego ruchu DML.
Pobierz oficjalny dokument już dziś Zarządzanie i automatyzacja PostgreSQL za pomocą ClusterControlDowiedz się, co musisz wiedzieć, aby wdrażać, monitorować, zarządzać i skalować PostgreSQLPobierz oficjalny dokumentMonitorowanie
Naprawdę powinieneś zainwestować w dobre rozwiązanie do monitorowania. Powinieneś monitorować ...
U abonenta:
- WSZYSTKIE wiadomości w dzienniku subskrybenta, które dotyczą logicznego wyjścia pracownika. Zainstalowanie narzędzia takiego jak tail_n_mail może w tym naprawdę pomóc. Konfiguracja, o której wiadomo, że działa:
Gdy otrzymamy alert z tail_n_mail, powinniśmy natychmiast rozwiązać problem.INCLUDE: ERROR: .*publisher.* INCLUDE: ERROR: .*exited with exit.* INCLUDE: LOG: .*exited with exit.* INCLUDE: FATAL: INCLUDE: PANIC:
- pg_stat_subscription. Pid nie powinien być pusty. Również opóźnienie powinno być niewielkie.
U wydawcy:
- pg_stat_replication. Powinno to mieć tyle wierszy, ile powinno być:Po jednym dla każdego podłączonego trybu gotowości replikacji strumieniowej (w tym węzłów subskrybentów i innych fizycznych trybów gotowości).
- pg_replication_slots dla slotu subskrybenta. To powinno być aktywne.
Ogólnie rzecz biorąc, potrzeba trochę czasu, zanim idealny serwer testowej bazy danych będzie działał bez problemów, ale kiedy już je wszystkie rozwiążesz, programiści będą Ci wdzięczni za jego posiadanie!