Slony-I jest fajną techniką replikacji danych między bazami PostgreSQL. W przeciwieństwie do techniki warm standby, która efektywnie polega na utrzymywaniu zapasowej bazy w stanie ciągłego odtwarzania inkrementalnego backupu bazy głównej, Slony-I potrafi replikować wybrane tabele i schematy (a nie wyłącznie całą bazę), docelowa baza może być cały czas używana (nawet replikowane tabele - w wariancie tylko do odczytu), a przede wszystkim możliwa jest replikacja między różnymi wersjami PostgreSQL (np. między bazą 8.1 a 8.3) i różnymi architekturami (i386 i amd64 to różne architektury, plików WAL między nimi nie przeniesiemy). Można zresztą replikować do bazy realizującej normalną obsługę innych tabel.
Dokumentacja Slony-I jest dość rozbudowana i obejmuje wiele trudnych przypadków, poniżej prosta recepta na replikację master-slave w konfiguracji dwumaszynowej.
Przygotowanie
Instalujemy Slony-I na obu uczestniczących w replikacji maszynach. Zwykle jest dostępne w paczce, jeśli nie, kompilujemy samodzielnie. Do kompilacji potrzebne są pasujące do używanej wersji PostgreSQL pakiety developerskie (paczka postgresql8.3-server-dev-8.3
lub podobna) a także bison
i flex
(błąd kompilacji informujący o braku plików to właśnie brak bisona lub flexa). Instalacja Slony-I obejmuje spory pakiet skryptów SQL, programy slon
i slonik
oraz dwie biblioteki dzielone rozszerzające funkcjonalność PostgreSQL (te ostatnie muszą być dopasowane do wersji motoru bazy danych).
Uwaga: na obu (wszystkich) maszynach uczestniczących w replikacji musi być używana ta sama wersja Slony-I (natomiast wersje PostgreSQL mogą być różne). Jeśli paczki się nie zgadzają, trzeba kompilować.
Jak to działa
Slony-I oparty jest - nieco upraszczając - na triggerach. W chwili aktywowania replikacji Slony definiuje na replikowanych tabelach triggery powodujące odkładanie modyfikacji do pomocniczych tabel Slony-ego a na tabelach docelowych triggery blokujące modyfikacje inne niż wynikające z replikacji. Demon slon
działający na docelowej maszynie cyklicznie podłącza się do bazy głównej i ściąga nowe dane. I właściwie tyle. Bardziej złożone scenariusze występują tylko w trakcie definiowania/modyfikowania schematu replikacji, przełączania węzłów itd. Rejestracja funkcjonuje poprawnie nawet gdy slon
zostanie na jakiś czas zatrzymany (czy nie może się połączyć do zdalnej bazy) - dane po prostu oczekują w tabeli pomocniczej i zostaną ściągnięte, gdy slon
wystartuje.
Działaniem triggerów i procesów Slony zarządzamy przy pomocy programu slonik
. Należy o nim myśleć jako o konsolce administracyjnej - kiedykolwiek chcemy cokolwiek zmienić w schemacie replikacji (od dodania nowej bazy czy przełączenia mastera, po zmianę hasła używanego przy dostępie do którejś z baz), piszemy skrypcik i każemy slonik
-owi go wykonać. slonik
powiadamia wszystkie zainteresowane węzły (technicznie - wpisuje odpowiednie notki do kontrolnych tabel Slony-ego) o tym jakie operacje mają wykonać. Język skryptów slonik
-a przypomina SQL, przykłady poniżej.
Slony-I instaluje też pakiet perlowych programików slonik_*
(tzw. slony_tools
), które po prostu potrafią generować typowe skrypty slonik
-a na podstawie pliku konfiguracyjnego i parametrów. Można próbować ich używać ale warto zawsze przejrzeć wygenerowany skrypt przed jego wykonaniem. W poniższym opisie z nich nie korzystam.
Szykujemy bazy
O ile docelowa baza nie istnieje, tworzymy ją (a także użytkownika będącego właścicielem tabel), np. (operacje wykonywane na maszynie docelowej):
$ createuser mydbowner $ createdb --encoding UTF8 --owner mydbowner MyDB
Przeglądamy pliki pg_hba.conf
i konfigurujemy je tak, by pozwalały na autoryzowany hasłem dostęp - lokalny, z pozostałych maszyn uczestniczących w replikacji, a także z maszyny na której będziemy pisać i uruchamiać skrypty slonika (najwygodniej - roboczego desktopu). Najczęściej będą to wpisy typu
hostssl MyDB all 127.0.0.1/32 md5 hostssl MyDB all 144.107.11.20/32 md5 hostssl MyDB all 151.32.211.50/32 md5
gdzie oczywiście umieszczamy właściwe adresy IP (na maszynie master - adres maszyny slave i roboczego desktopu, na maszynie slave - adres mastera i desktopu).
Jeśli dostęp protokołami Postgresa przez sieć nie jest możliwy, można wykorzystać tunele ssh. Szczegółowo tego tu nie opisuję, ale staram się tłumaczyć gdzie i jak podajemy namiary na poszczególne bazy danych.
Tworzymy też (puste) tabele o strukturze zgodnej z strukturą replikowanych tabel. Prosty sposób:
$ pg_dump --schema-only --host master.host --port 5432 MasterDB > schema.sql
(plik schema.sql
możemy tu przejrzeć i np. usunąć niepotrzebne do replikacji tabele czy schematy)
$ psql --host slave.host --port 5432 MyDb < schema.sql
Tworzymy wreszcie użytkowników przeprowadzających replikację w obu bazach. Np. (tu nadaję im nieoczekiwaną nazwę slony
)
$ createuser --host=master.host --port=5432 \ --superuser --pwprompt --encrypted slony $ createuser --host=slave.host --port=5433 \ --superuser --pwprompt --encrypted slony
Nazwy użytkowników nie muszą być identyczne, hasła podajemy dowolne, jedne i drugie zapisujemy.
OK, koniec przygotowań.
Definiujemy klaster
Zaczynamy od napisania (na maszynie roboczej) skrypciku preamble.slonik
. Jest to zbiór stałych, które będziemy włączać do wszystkich pozostałych skryptów. Wygląda tak:
cluster name = myrepl; define node_mydb_master 1; define node_mydb_slave 2; define set_myapp 11; node @node_mydb_master admin conninfo='host=master.db port=5432 dbname=MyMasterDb user=slony password=secret'; node @node_mydb_slave admin conninfo='host=slave.db port=5432 dbname=MyDb user=slony password=secretoo';
Nazwa klastra to nazwa całego układu replikacji (technicznie - Slony stworzy w wszystkich bazach swój schemat o nazwie opartej na nazwie klastra, tu - _myrepl
). Powinna jakoś opisywać zbiór baz w ramach których replikujemy (raczej nie nazywajmy go od aplikacji, w jednym klastrze możemy chcieć zdefiniować kilka niezależnych schematów replikacyjnych o ile tylko toczą się w grupie tych samych baz).
Polecenia define
pozwalają definiować stałe (do których potem odwołujemy się przy pomocy @
), są opcjonalne (można pisać cyferki) ale bardzo zwiekszają czytelność skryptów.
Polecenia node
zapisują jak do poszczególnych baz danych ma się dostawać program slonik
, tj. jak się do nich dostajemy z desktopu administratora (z maszyny, z której odpalamy skrypty slonika).
Następnie piszemy skrypt init_cluster.slonik
. Wygląda tak:
include <preamble.slonik> echo 'Set up replication nodes'; init cluster (id = @node_mydb_master, comment = 'MyDb master'); store node (id = @node_mydb_slave, comment = 'MyDb slave'); echo 'Define paths'; store path (server = @node_mydb_slave, client = @node_mydb_master, conninfo = 'host=slave.host dbname=MyDb user=slony port=5432 password=secret'); store path (server = @node_mydb_master, client = @node_mydb_slave, conninfo = 'host=master.host dbname=MasterDb user=slony port=5432 password=secrettoo'); echo 'Please start a slon replication daemon for each node';
Polecenia init cluster
i store node
tworzą schematy Slony-ego w poszczególnych bazach danych. init cluster
wołamy dla pierwszego węzła, dla pozostałych store node
.
Polecenia store path
zapisują informacje w jaki sposób procesy slon
mają się dostawać do zdalnych baz (tu - jak slon
z mastera ma się dostawać do slave i jak slon
z slave ma się dostawać do mastera). Jeśli w replikacji uczestniczy więcej maszyn, musimy dodać wszystkie kombinacje.
Absolutnie niezbędne jest zapisanie sposobu w jaki
slon
z slave ma się dostawać do mastera. Odwrotna ścieżka nie będzie przez większość czasu używana ale może być potrzebna w trakcie wykonywania operacjislonik
-a (jak definiowanie schematu aplikacji, a zwłaszcza przełączanie węzłów). Nie próbowałem uruchamiać Slony-ego bez podania ścieżki od mastera do slave.
Napisany skrypt uruchamiamy:
$ slonik init_cluster.slonik
Po chwili nasz klaster powinien zostać zainicjowany (możemy sprawdzić, czy pojawiły się w obu bazach schematy _myrepl
). Oczywiście nic się jeszcze nie replikuje.
Uruchamiamy procesy slon
Startujemy procesy slon
na maszynach master i slave. Na Debianie/Ubuntu najwygodniej zrobić to tak:
$ mkdir /etc/slony1/myrepl $ zcat /usr/share/doc/slony1-bin/examples/slon.conf-sample.gz > /etc/slony1/myrepl/slon.conf $ vim /etc/slony1/myrepl/slon.conf
Edytując powyższy plik musimy poprawić:
cluster_name
(wpisujemy to samo co wpreamble.slonik
)conn_info
(kolejne miejsce gdzie wpisujemy namiary na bazę, tym razem jest to adres i hasło do bazy lokalnej dla uruchamianego procesuslon
)
Np.
cluster_name='myrepl' conn_info='host=localhost port=5432 dbname=MyDb user=slony password=secret'
Reszta ustawień dotyczy strojenia wydajności - od częstostliwości sprawdzania czy są nowe dane do replikacji, po rózne limity pamięciowe. Warto to przejrzeć, ale niekoniecznie w tym momencie.
Na koniec uruchamiamy proces slon. Np
$ /etc/init.d/slony1 start
W innych dystrybucjach/konfiguracjach sposób uruchamiania slon-ia może się różnić. Tak czy siak chodzi o przygotowanie pliku slon.conf wdg opisanej metody i doprowadzenie do uruchamiania się
slon -f slon.conf
. Można go uruchamiać nawet ręcznie.
Oczywiście powyższą operację powtarzamy zarówno na master, jak na slave.
Definiujemy replikowane dane
Piszemy kluczowy skrypt - create_set.slonik
. Przykład:
include <preamble.slonik> try { create set (id = @set_myapp, origin = @node_mydb_maser, comment = 'Myapp replication from production to backup'); } on error { echo 'Failed to create set'; exit -1; } echo 'Set initialized'; # Tables echo 'Adding tables'; set add table (set id = @set_myapp, origin = @node_mydb_master, id = 101, full qualified name = 'public.users', comment = 'Table public.users'); echo '... public.users'; set add table (set id = @set_myapp, origin = @node_mydb_master, id = 102, full qualified name = 'public.articles', comment = 'Table public.articles'); echo '... public.articles'; # Sequences echo 'Adding sequences'; set add sequence (set id = @set_myapp, origin = @node_mydb_master, id = 201, full qualified name = 'public.users_user_id_seq', comment = 'Sequence users/user_id'); echo '... public.users_user_id_seq'; echo 'All tables and sequences added';
Wymieniamy tu wszystkie tabele i sekwencje podlegające replikacji. Numerki id
nadajemy dowolnie, byle były różne.
Proponuję konwencję w której węzły identyfikuję liczbami 1,2,3..., zbiory replikacyjne 11,12, ... a tabele 101, 102,... Nie jest to konieczne, dokumentacja Slony-ego wszędzie używa małych liczb (1,2,3) ale upraszcza życie - widząc jakiś numerek od razu wiem, do jakiego obiektu się odnosi.
Wszystkie replikowane tabele muszą mieć klucz główny!!! Jeśli go nie mają, przed rozpoczęciem replikacji trzeba go dodać.
Jak już pisałem, zbiorów replikacyjnych możemy stworzyć wiele. Najsensowniejsza konwencja to oczywiście jeden zbiór - jedna aplikacja z jej tabelami ale czasem bywają stosowane inne granulacje.
Jeśli występują jakieś klucze obce czy inne więzy integralności, połączone nimi tabele muszą należeć do jednego zbioru replikacji!
Dwa zbiory można połączyć, istniejącego zbioru nie można podzielić.
Po dokładnym zweryfikowaniu, czy uwzględniliśmy wszystkie potrzebne tabele i sekwencje i czy poprawnie je nazwaliśmy, uruchamiamy skrypt:
$ slonik create_set.slonik
Gdy skrypt się zakończy, replikacja jeszcze nie działa - ale jest już gotowa do uruchomienia.
Uruchomienie replikacji
Piszemy jeszcze jeden, tym razem krótki, skrypt slonika - subscribe.slonik
:
include <preamble.slonik> try { subscribe set (id = @set_myapp, provider = @node_mydb_master, receiver = @node_mydb_slave, forward = yes); } on error { exit 1; } echo 'Subscribed!';
Uruchamiamy go:
$ slonik subscribe.slonik
Po krótkiej chwili replikacja powinna się rozpocząć. Zacznie się od skasowania wszelkich danych z tabel w bazie docelowej i pokopiowania do nich całych tabel z bazy źródłowej (jeśli baza źródłowa jest duża, może to potrwać), w trakcie czego tabele docelowe pozostają zablokowane (np. próby zrobienia SELECT
na aktualnie kopiowanej tabeli zawisną czekając na zwolnienie blokady). Gdy inicjalne kopiowanie się zakończy, Slony przejdzie w normalny tryb inkrementalnej replikacji i na docelowych tabelach będzie można wykonywać zapytania (ale nie modyfikacje, te skończą się błędem).
Proces można śledzić w logach procesu slon
(na Debianie/Ubuntu - /var/log/slony1
).
Przez cały czas definiowania i uruchamiania replikacji bazę master można używać bez ograniczeń. Warto jedynie wiedzieć, że jeśli na masterze wykonuje się aktualnie jakieś bardzo długotrwałe zapytanie (np. duży raport) - niekoniecznie na replikowanych tabelach, gdziekolwiek w ramach instancji PostgreSQL - Slony będzie czekał na jego zakończenie (technicznie: na zakończenie się wszystkich transakcji o identyfikatorze mniejszym niż bieżący w chwili gdy Slony aktywował triggery).
Dla upewnienia się, że wszystko działa poprawnie, robimy jakieś zmiany w bazie na masterze (tj. po prostu używamy jej, np. dopisujemy artykuł czy komentarz) a po chwili sprawdzamy w bazie slave czy odpowiedni rekord się pojawił.
Koniec. Na razie (kiedyś może popiszę o migrowaniu węzłów i innych zastosowaniach).
Aha: plik preamble.slonik
(pozostałe pliki .slonik
zresztą też) z wielką atencją zachowujemy sobie w bezpiecznym miejscu, na pewno będą jeszcze potrzebne.