Hlavní navigace

Slony1 - Replikace pro PostgreSQL (4)

19. 8. 2004
Doba čtení: 7 minut

Sdílet

Dneska si poprvé zareplikujeme...

Replikace dat v clusteru

Replikace dat neprobíhá po jednotlivých tabulkách, ale po setech. Set je skupina příbuzných tabulek a sekvencí, která se replikuje jako celek. Každý set má právě jeden zdrojový uzel, na kterém je dovolena jeho modifikace. V našem případě vytvoříme set z tabulek vytvořených programem pgbench:

master=# \d
         List of relations
 Schema |   Name   | Type  | Owner
--------+----------+-------+-------
 public | accounts | table | slony
 public | branches | table | slony
 public | history  | table | slony
 public | tellers  | table | slony
(4 rows)

Slony vyžaduje, aby každá tabulka určená k replikaci měla alespoň jeden UNIQUE NOT NULL index, který bude používán k jednoznačnému určení změněné řádky. Primární klíče vyhovují této podmínce. V našem případě tabulka history tuto podmínku nesplňuje.

ster=# \d history
              Table "public.history"
 Column |            Type             | Modifiers
--------+-----------------------------+----------
 tid    | integer                     |
 bid    | integer                     |
 aid    | integer                     |
 delta  | integer                     |
 mtime  | timestamp without time zone |
 filler | character(22)               |

Slony umožňuje tento klíč k tabulce automaticky přidat. Můžeme jej ovšem k tabulce přidat i sami, což je vzhledem k pravidlům správného návrhu tabulek preferovaný postup. V následujícím příkladě to ovšem necháme na Slony1.

TABLE ADD KEY ( NODE ID = 1, FULL QUALIFIED NAME = 'public.history' ); 

Pokud již máme v tabulce data (což nevadí), nebude u stávajících řádků sloupec _Slony-I_dharma_rowID naplněn hodnotami, což je způsobeno chováním PosgtreSQL verzí 7.4 a starších.

Tabulka po provedení příkazu:

master=# \d history
                                        Table "public.history"
        Column         |            Type             | Modifiers
-----------------------+-----------------------------+ ---------
 tid                   | integer                     |
 bid                   | integer                     |
 aid                   | integer                     |
 delta                 | integer                     |
 mtime                 | timestamp without time zone |
 filler                | character(22)               |
 _Slony-I_dharma_rowID | bigint                      | default nextval('"_dharma".sl_rowid_seq'::text) 

Nyní již můžeme vytvořit set tabulek. Povšimněte si key=serial u tabulky history. Dále je nutné poznamenat, že replikace se provádí nejen na základě jména tabulky, ale i na základě schema. Nelze tedy replikovat tabulku master: public.accounts do tabulky slave: slony.accounts.

CREATE SET ( ID = 1, ORIGIN = 1, COMMENT = 'PGbench tables' );
SET ADD TABLE ( SET ID = 1, ORIGIN = 1,
ID = 1, FULL QUALIFIED NAME = 'public.accounts',
COMMENT = 'Table accounts' );
SET ADD TABLE ( SET ID = 1, ORIGIN = 1,
ID = 2, FULL QUALIFIED NAME = 'public.branches',
COMMENT = 'Table branches' );
SET ADD TABLE ( SET ID = 1, ORIGIN = 1,
ID = 3, FULL QUALIFIED NAME = 'public.tellers',
COMMENT = 'Table tellers' );
SET ADD TABLE ( SET ID = 1, ORIGIN = 1,
ID = 4, FULL QUALIFIED NAME = 'public.history',
KEY = SERIAL, COMMENT = 'Table history' ); 

Kromě aktualizace replikačních katalogů byly při vytvoření setu doplněny hodnoty rowID pro stávající řádky v tabulce history. Od této chvíle jsou již všechny námi prováděné změny v setu protokolovány.

Spustíme na chvíl pgbench a vyrobíme nějaké změny v datech

%pgbench -t 1000 master
starting vacuum...end.
transaction type: TPC-B (sort of)
scaling factor: 5
number of clients: 1
number of transactions per client: 1000
number of transactions actually processed: 1000/1000
tps = 26.879933 (including connections establishing)
tps = 26.902972 (excluding connections establishing)

Využijeme vlastnosti programu pgbench a zjistíme, jaký má logování změn overhead. Na chvíli zrušíme námi vytvořený set příkazem DROP SET (id = 1, origin=1);, který vrátí tabulky do původního stavu a zopakujeme pgbench. Není to tak zlé, zdržení je zhruba 30 procent.

%pgbench -t 1000 master
starting vacuum...end.
transaction type: TPC-B (sort of)
scaling factor: 5
number of clients: 1
number of transactions per client: 1000
number of transactions actually processed: 1000/1000
tps = 38.505206 (including connections establishing)
tps = 38.559025 (excluding connections establishing)

Na uzlu slave1 se přihlásíme k odběru setu 1 z uzlu master. Parametr forward určuje, zda bude receiver poskytovat tento set k replikaci dalším uzlům, nebo zda bude jen koncovým odběratelem.

SUBSCRIBE SET ( ID = 1, PROVIDER = 1, RECEIVER = 2, FORWARD = NO ); 

slave1 pošle masteru zprávu SUBSCRIBE SET a master na ni odpoví zprávou ENABLE SUBSCRIPTION. Slave1 nato začne vytvářet full copy setu 1.

DEBUG2 remoteWorkerThread_1: Received event 1,431 ENABLE_SUBSCRIPTION
DEBUG1 copy_set 1
DEBUG1 remoteWorkerThread_1: connected to provider DB
DEBUG2 remoteWorkerThread_1: copy table public.accounts
ERROR  remoteWorkerThread_1: "select "_dharma".setAddTable_int(1, 1, 'public.accounts', 'accounts_pkey', 'Table accounts'); " PGRES_FATAL_ERROR ERROR:  Slony-I: setAddTable(): table public.accounts not found
WARN   remoteWorkerThread_1: data copy for set 1 failed - sleep 15 seconds 

Zde jsme narazili na omezení současné verze Slony1. Slony1 neumí vytvořit v databázi slave1 potřebné tabulky na základě dotazu ze systémového katalogu databáze master. Toto omezení je v praxi velmi otravné. Je nutné například vydumpovat definici schematu obsahujícího definice tabulek k replikaci z databáze master, a tuto definici pak nahrát do databáze slave1. Pokud máme již vytvořený set (jako v tomto případě), bude vydumpovaná definice schematu obsahovat i replikační trigery, které v žádném případě nechceme naimporovat do databáze slave1. Těchto trigerů se můžeme zbavit příkazem DROP SET, který ovšem bude mít za následek to, že až se ostatní uzly znovu přihlásí k odběru setu SUBSCRIBE SET, budou muset překopírovat všechny data. Takže vydumpujeme schema např.: pg_dump -s –schema=public -U pgsql master, vyhážeme z něj všechno kromě definice tabulek, které nás zajímají, a poté nahrajeme do databáze slave1. Toto schema si v každém případě uschovejte pro pozdější použití.

Po vytvoření tabulek se již replikace rozběhne. Celá akce začne opětovným zpracováním události ENABLE_SUBSCRIP­TION. Nejdříve se provede plná kopie všech tabulek.

DEBUG2 remoteWorkerThread_1: Received event 1,431 ENABLE_SUBSCRIPTION
DEBUG1 copy_set 1
DEBUG1 remoteWorkerThread_1: connected to provider DB
DEBUG2 remoteWorkerThread_1: copy table public.accounts
DEBUG2 remoteWorkerThread_1: 47895102 bytes copied for table public.accounts
DEBUG2 remoteListenThread_1: queue event 1,489 SYNC
DEBUG2 remoteWorkerThread_1: 239.518 seconds to copy table public.accounts
DEBUG2 remoteWorkerThread_1: copy table public.branches
DEBUG2 remoteWorkerThread_1: 60 bytes copied for table public.branches
DEBUG2 remoteWorkerThread_1: 0.384 seconds to copy table public.branches
DEBUG2 remoteWorkerThread_1: copy table public.tellers
DEBUG2 remoteWorkerThread_1: 691 bytes copied for table public.tellers
DEBUG2 remoteWorkerThread_1: 0.148 seconds to copy table public.tellers
DEBUG2 remoteWorkerThread_1: copy table public.history
DEBUG2 remoteWorkerThread_1: 62368 bytes copied for table public.history
DEBUG2 remoteWorkerThread_1: 0.943 seconds to copy table public.history
DEBUG2 remoteWorkerThread_1: copy_set SYNC found, use event seqno 483.
DEBUG2 remoteWorkerThread_1: 0.253 seconds to build initial setsync status
DEBUG1 remoteWorkerThread_1: disconnected from provider DB
DEBUG1 copy_set 1 done in 241.442 seconds
CONFIG enableSubscription: sub_set=1 

Agent databáze slave1 si vytvoří další spojení do databáze master a při obdržení SYNC zprávy provádí periodickou synchronizaci tabulek.

DEBUG2 remoteWorkerThread_1: SYNC 545 processing
DEBUG1 remoteWorkerThread_1: connected to data provider 1 on 'dbname=master host=localhost user=slony password=iehovah'
DEBUG2 remoteWorkerThread_1: syncing set 1 with 4 table(s) from provider 1
DEBUG2 remoteHelperThread_1_1: 0.029 seconds delay for first row
DEBUG2 remoteHelperThread_1_1: 0.033 seconds until close cursor
DEBUG2 remoteWorkerThread_1: new sl_rowid_seq value: 1000000000002323
DEBUG2 remoteWorkerThread_1: SYNC 545 done in 0.192 seconds 

Agent už nepřistupuje do tabulek v master databázi, ale prohlíží si protokolační tabulku sl_log1 (případně 2, tabulky jsou rotovány). Odtud si vybírá poslední dosud nezpracované změny v setech, do kterých je přihlášen. Zápis změn v datech zabezpečují AFTER EACH ROW triggery vytvořené replikačním systémem při tvorbě setu. Záznamy pro delete, insert, update vypadají takto:

master=# SELECT * from _dharma.sl_log_1;
 log_origin | log_xid | log_tableid | log_actionseq | log_cmdtype | log_cmddata
------------+---------+ ------------+ --------------+ ------------+ ---------------- --------------- ----------
          1 | 1937933 |           4 |        453998 | D           | "_Slony-I_dharma_rowID"='1000000000082407'
          1 | 1937947 |           4 |        459029 | I           | (tid,bid,aid
,delta,mtime,"_Slony-I_dharma_rowID") values ('15','5','226186','894','2004-07-0
2 15:13:45.721412','1000000000087411')
          1 | 1937948 |           1 |        459030 | U           | abalance='47
3' where aid='74268' 

Pro sekvence takto:

master=# SELECT * from sl_seqlog ;
seql_seqid | seql_origin | seql_ev_seqno | seql_last_value
-----------+-------------+---------------+-----------------
         0 |           1 |          2031 | 1000000000087402
         0 |           1 |          2032 | 1000000000087402
         0 |           1 |          2033 | 1000000000087402
         0 |           1 |          2034 | 1000000000087402

Replikace neprobíhá kontinuálně, ale po tzv. sync událostech. Sync události jsou periodicky generovány agentem starající se o příslušnou databázi. Program slon má dva parametry, které nastavují minimální a maximální dobu mezi generací sync událostí. Standardně je minimum 10 sekund a maximum 60 sekund. V případě databázové aktivity jsou sync události generovány tak rychle, jak jen nastavení dovoluje. a v opačném případě zase tak pomalu jak je to jen možné. Pokud byla databáze neaktivní déle, než je minimální doba, je následující SYNC událost vygenerována okamžitě po skončení transakce. Aktivitou master databáze je v tomto případě myšlena změna dat. Read-only transakce replikační mechanismus ignoruje.

Na straně slave1 databáze jsou jednotlivé SYNC události zpracovávány tak rychle, jak je to jen možné. Pokud čeká na zpracování více SYNC událostí, jsou tyto události sloučeny do skupiny a zpracovány jako jeden celek. Zpracování SYNC události X zahrnuje zpracování i všech událostí předcházejících. Pokud slave databáze není poskytovatelem dat pro jiné slave databáze, a proto nemusí prováděné změny ve svých datech protokolovat, zpracovává data zhruba 2× rychleji, než je master databáze stačí dodávat.

Slony1 nesleduje změny ve struktuře replikovaných tabulek, protože současné verze PostgreSQL už neumožňují definici uživatelských triggerů nad systémovým katalogem. V případě změny struktury je potřeba buďto set zrušit a pak znovu celý zreplikovat, nebo risknout změnu ručně na všech uzlech současně. V případě neúspěchu se nic moc neděje, set se zruší a zreplikuje celý. Toto je hlavní nevýhoda add-on replikačních systémů oproti replikacím zabudovaných přímo v databázových serverech.

root_podpora

Protože je slony single master replikační systém, není možné replikovaná data měnit jinde než v master databázi.

(hsn@ttyp0):~% pgbench -U slony slave1
starting vacuum...ERROR:  Slony-I: Table history is replicated and
cannot be modified on a subscriber node 

Po dnešním dílu již umíme replikovat data. V dalším dílu se podíváme na správu clusteru.

Byl pro vás článek přínosný?