Replizieren der ersten Datenbank In diesem Beispiel werden wir versuchen eine neu angelegte "pgbench" Datenbank zu replizieren. Die Grundlagen zur Replikation einer bestehenden Datenbank werden ebenfalls erläutert wir empfehlen jedoch, Slony-I anhand einer brandneuen, nicht produktiven Datenbank kennenzulernen. Die Slony-I Replikationsengine basiert auf Triggern, daher ist es möglich Datenbanken (oder auch Teile davon) zu replizieren die innerhalb der selben "postmaster" Instanz laufen. In diesem Beispiel wird nun die auf localhost (Master) laufende "pgbench" Datenbank auf die ebenfalls auf localhost laufende Slave "pgbench" Datenbank repliziert. Bezüglich der bestehenden PostgreSQL Installation werden einen Reihe von Annahmen getroffen: 1. "tcpip_socket" ist auf "true" gesetzt in Ihrer postgresql.conf 2. localhost ist auf "trust" gesetzt in der pg_hba.conf Der REPLICATIONUSER muss ein PostgreSQL Superuser sein. Typischerweise ist dies "postgres" oder "pgsql". Zusätzlich sollten folgende Umgebungsvariablen in Ihrer Shell gesetzt werden: CLUSTERNAME=slony_example MASTERDBNAME=pgbench SLAVEDBNAME=pgbenchslave MASTERHOST=localhost SLAVEHOST=localhost REPLICATIONUSER=pgsql PGBENCHUSER=pgbench Hier sind ein paar Beispiele um diese Variablen in verschiedenen Shells zu setzen: bash/sh: export CLUSTERNAME=slony_example (t)csh: setenv CLUSTERNAME slony_example Erzeugen des "pgbench" Benutzers: createuser -A -D $PGBENCHUSER Vorbereiten der Datenbanken: createdb -O $PGBENCHUSER -h $MASTERHOST $MASTERDBNAME createdb -O $PGBENCHUSER -h $SLAVEHOST $SLAVEDBNAME pgbench -i -s 1 -U $PGBENCHUSER -h $MASTERHOST $MASTERDBNAME Da Slony-I die Existenz von pl/pgSQL in den beiden Datenbanken voraussetzt, sollte dieses nun installiert werden. Falls pl/pgSQL bereits früher in der template1 Datenbank installiert wurde kann dieser Schritt jedoch übersprungen werden. createlang -h $MASTERHOST plpgsql $MASTERDBNAME Slony-I kopiert zum jetzigen Zeitpunkt keine Tabellendefinitionen von der Masterdatenbank, wenn ein Slave hinzugefügt wird. Daher ist es nötig diese Daten manuell zu importieren. Im folgenden Beispiel wird pg_dump dafür verwendet. pg_dump -s -U $REPLICATIONUSER -h $MASTERHOST $MASTERDBNAME | psql -U $REPLICATIONUSER -h $SLAVEHOST $SLAVEDBNAME Um zu demonstrieren wie ein Slony-I Slave im laufenden Betrieb hinzugefügt werden kann wird nun "pgbench" gestartet. Wenn sie die pgbench Applikation im Vordergrund eines eigenen Terminalfensters ablaufen lassen können Sie es jederzeit stoppen und danach erneut mit anderen Parametern starten. Die entsprechenden Umgebungsvariablen müssen natürlich erneut exportiert werden sodass sie auch in dieser Sitzung verfügbar sind. Ein typisches Aufruf von "pgbench" könnte so aussehen: pgbench -s 1 -c 5 -t 1000 -U $PGBENCHUSER -h $MASTERHOST $MASTERDBNAME Dieser Aufruf wird pgbench mit 5 parallelen Clients, wobei jeder 1000 Transaktionen durchführt, starten. Dieser Vorgang wird die pgbench Datenbank die auf localhost läuft und den entsprechenden PGBENCHUSER verwenden. Konfiguration der Datenbanken für die Replikation Das Erzeugen der Konfigurationstabellen, Stored Procedures, Trigger und der Konfiguration geschieht mit Hilfe des slonik Tools. Es handelt sich hierbei um ein spezialisiertes Skriptinghilfsmittel, welches vorallem eine Reihe von Stored Procedures in den Master- bzw. Slavedatenbanken aufruft. Das Skript zum Erzeugen der initialen Konfiguration für diese einfache Master-Slave Installation unserer pgbench Datenbank sieht wie folgt aus: #!/bin/sh slonik <<_EOF_ #-- # Festlegung des Namespaces den das Replikationssystem verwendet. In unserem # Beispiel "slony_example" #-- cluster name = $CLUSTERNAME; #-- # Die "admin conninfo"'s werden von slonik dazu verwendet um der Reihe nach # zu jedem Knoten auf jeder Seite des Clusters zu verbinden. Die Syntax # entspricht der von PQconnectdb in der C-API. # -- node 1 admin conninfo = 'dbname=$MASTERDBNAME host=$MASTERHOST user=$REPLICATIONUSER'; node 2 admin conninfo = 'dbname=$SLAVEDBNAME host=$SLAVEHOST user=$REPLICATIONUSER'; #-- # Initialisierung des ersten Knotens, die ID dieses Knotens muss 1 sein. # Dadurch wird ein Schema _$CLUSTERNAME erzeugt welches alle # Replikationssystem-spezifischen Objekte enthält. #-- init cluster ( id=1, comment = 'Master Node'); #-- # Da die history Tabelle keine primären Schlüssel oder einen anderen # eindeutigen Constraint enthält, der zu Identifizierung einer # Tabellenreihe dienen könnte, muss ein entsprechender hinzugefügt werden. # Das folgende Kommando fügt eine bigint Spalte mit der Bezeichnung # _Slony-I_$CLUSTERNAME_rowID zur Tabelle hinzu. # Diese Spalte wird einen Standardwert von # nextval('_$CLUSTERNAME.s1_rowid_seq'), sowie UNIQUE und NOT NULL # Einschränkungen, besitzen. # Alle bereits bestehenden Zeilen werden mit einer Nummer initialisiert. #-- table add key (node id = 1, fully qualified name = 'public.history'); #-- # Slony-I teilt Tabellen in "Sets" ein. Die kleinste Einheit zu der ein # Knoten subskribieren kann ist ein Set. # Die folgenden Kommandos erzeugen ein Set welches alle 4 pgbench # Tabellen beinhaltet. Der Master bzw. die Quelle dieses Sets ist # der Knoten 1. # Es ist für jede Tabelle die repliziert werden soll, nötig ein # entsprechendes "set add table()" zu verwenden. #-- create set (id=1, origin=1, comment='All pgbench tables'); set add table (set id=1, origin=1, id=1, fully qualified name = 'public.accounts', comment='accounts table'); set add table (set id=1, origin=1, id=2, fully qualified name = 'public.branches', comment='branches table'); set add table (set id=1, origin=1, id=3, fully qualified name = 'public.tellers', comment='tellers table'); set add table (set id=1, origin=1, id=4, fully qualified name = 'public.history', comment='history table', key = serial); #-- # Erzeugung des zweiten Knotens (des Slaves). Weiters folgen die nötigen # Informationen wie die beiden Knoten miteinander kommunizieren sollen. #-- store node (id=2, comment = 'Slave node'); store path (server = 1, client = 2, conninfo='dbname=$MASTERDBNAME host=$MASTERHOST user=$REPLICATIONUSER'); store path (server = 2, client = 1, conninfo='dbname=$SLAVEDBNAME host=$SLAVEHOST user=$REPLICATIONUSER'); store listen (origin=1, provider = 1, receiver =2); store listen (origin=2, provider = 2, receiver =1); _EOF_ Sollte pgbench jetzt nicht mehr laufen, sollte es erneut gestartet werden. Zu diesem Zeitpunkt existieren nun zwei komplett vorbereitete Datenbanken. Eine davon ist die Masterdatenbank in dem pgbench fleissig auf Daten zugreift und diese kontinuierlich verändert. Es ist nun an der Zeit die Replikationsdaemons zu starten. Der Befehl zum starten der Replikationengine auf dem $MASTERHOST ist: slon "$CLUSTERNAME dbname=$MASTERDBNAME user=$REPLICATIONUSER host=$MASTERHOST" Zum Start des Replikationssystems auf dem Knoten 2 (also dem Slave): slon "$CLUSTERNAME dbname=$SLAVEDBNAME user=$REPLICATIONUSER host=$SLAVEHOST" Obwohl der "slon" Prozess nun auf beiden Knoten läuft, und beide Instanzen eine Reihe von Diagnosezeilen und anderer Ausgaben erzeugen, werden noch keine Daten repliziert. Die Informationen die angezeigt werden, dienen einzig zur Syncronisation der Clusterkonfiguration zwischen den beiden "slon" Prozessen. Zum tatsächlichen Starten der Replikation der vier pgbench-Tabellen vom Master mit der Node id 1 zum Slave mit der Node id 2, sollte folgendes Skript ausgeführt werden. #!/bin/sh slonik <<_EOF_ # ---- # Festlegung des Namespaces für das Replikationssystem # ---- cluster name = $CLUSTERNAME; # ---- # "admin conninfo"'s werden vom slonik-Programm zur Verbindung # zu den Datenbanken der einzelnen Knoten verwendet. # Dies entspricht den PQconnectdb Argumenten die für Verbindungen # vom Arbeitsplatz des Administrators - wo slonik ausgeführt # wird - aus nötig sind. # ---- node 1 admin conninfo = 'dbname=$MASTERDBNAME host=$MASTERHOST user=$REPLICATIONUSER'; node 2 admin conninfo = 'dbname=$SLAVEDBNAME host=$SLAVEHOST user=$REPLICATIONUSER'; # ---- # Knoten 2 subskribiert Set 1 # ---- subscribe set ( id = 1, provider = 1, receiver = 2, forward = no); _EOF_ Wenige Sekunden später wird der auf $SLAVEHOST laufende Replikationsdaemon beginnen, den aktuellen Inhalt der 4 zu replizierenden Tabellen, zu kopieren. Während diesem Vorgang wird klarerweise die pgbench Anwendung weiterhin die Datenbank modifizieren. Sobald der initiale Kopiervorgang abgeschlossen ist, wird der Replikationsdaemon auf dem $SLAVEHOST beginnen "aufzuholen" indem er beginnt das sich ansammelnde Replikationslog abzuarbeiten. Dieser Vorgang geschied Schrittweise, jeweils werden 10 Sekunden an Anwendungsarbeit repliziert. Abhängig von der Leistungsfähigkeit der beiden involvierten Systeme, der aktuellen Transaktionslast, der Grösse sowie des Tunings der Datenbanken, kann dieser Vorgang eine Sache von Minuten, Stunden oder auch einer Ewigkeit sein. Sie haben nun erfolgreich Ihr erstes Master-Slave Replikationssystem installiert, die beiden Datenbanken enthalten nun identische Daten. Soweit die Theorie. In der Praxis schadet es nicht zu überprüfen ob die beiden Datensets wirklich identisch sind. Das folgende Skript erzeugt sortierte Dumps der beiden Datenbanken und vergleicht diese. Stellen Sie sicher das "pgbench" seinen Testlauf beendet hat und die slon-Instanzen aufgeholt haben bevor Sie dieses Skript ausführen. #!/bin/sh echo -n "**** comparing sample1 ... " psql -U $REPLICATIONUSER -h $MASTERHOST $MASTERDBNAME >dump.tmp.1.$$ <<_EOF_ select 'accounts:'::text, aid, bid, abalance, filler from accounts order by aid; select 'branches:'::text, bid, bbalance, filler from branches order by bid; select 'tellers:'::text, tid, bid, tbalance, filler from tellers order by tid; select 'history:'::text, tid, bid, aid, delta, mtime, filler, "_Slony-I_${CLUSTERNAME}_rowID" from history order by "_Slony-I_${CLUSTERNAME}_rowID"; _EOF_ psql -U $REPLICATIONUSER -h $SLAVEHOST $SLAVEDBNAME >dump.tmp.2.$$ <<_EOF_ select 'accounts:'::text, aid, bid, abalance, filler from accounts order by aid; select 'branches:'::text, bid, bbalance, filler from branches order by bid; select 'tellers:'::text, tid, bid, tbalance, filler from tellers order by tid; select 'history:'::text, tid, bid, aid, delta, mtime, filler, "_Slony-I_${CLUSTERNAME}_rowID" from history order by "_Slony-I_${CLUSTERNAME}_rowID"; _EOF_ if diff dump.tmp.1.$$ dump.tmp.2.$$ >$CLUSTERNAME.diff ; then echo "success - databases are equal." rm dump.tmp.?.$$ rm $CLUSTERNAME.diff else echo "FAILED - see $CLUSTERNAME.diff for database differences" fi Für den Fall das dieses Skript "FAILED" zurückliefert, kontaktieren Sie bitte die Entwickler unter http://slony.info/.