PostgreSQL er en af de mest avancerede open source-databaser i verden med en masse fantastiske funktioner. En af dem er Streaming Replication (Physical Replication), som blev introduceret i PostgreSQL 9.0. Det er baseret på XLOG-poster, som overføres til destinationsserveren og anvendes der. Det er dog klyngebaseret, og vi kan ikke lave en enkelt database eller enkelt objekt (selektiv replikering) replikering. Gennem årene har vi været afhængige af eksterne værktøjer som Slony, Bucardo, BDR osv. til selektiv eller delvis replikering, da der ikke var nogen funktion på kerneniveauet før PostgreSQL 9.6. PostgreSQL 10 kom dog med en funktion kaldet logisk replikering, hvorigennem vi kan udføre replikering på database-/objektniveau.
Logisk replikering replikerer ændringer af objekter baseret på deres replikeringsidentitet, som normalt er en primær nøgle. Det er anderledes end fysisk replikering, hvor replikering er baseret på blokke og byte-for-byte replikering. Logisk replikering behøver ikke en nøjagtig binær kopi på destinationsserversiden, og vi har mulighed for at skrive på destinationsserveren i modsætning til fysisk replikering. Denne funktion stammer fra det pglogiske modul.
I dette blogindlæg skal vi diskutere:
- Sådan virker det - Arkitektur
- Funktioner
- Brugstilfælde – når det er nyttigt
- Begrænsninger
- Sådan opnår du det
Sådan virker det - logisk replikeringsarkitektur
Logisk replikering implementerer et publicerings- og abonnementskoncept (Publication &Subscription). Nedenfor er et arkitektonisk diagram på højere niveau om, hvordan det fungerer.
Grundlæggende logisk replikeringsarkitektur
Udgivelsen kan defineres på masterserveren, og den node, hvorpå den er defineret, omtales som "udgiveren". Udgivelse er et sæt ændringer fra en enkelt tabel eller gruppe af tabeller. Det er på databaseniveau, og hver publikation findes i én database. Flere tabeller kan tilføjes til en enkelt publikation, og en tabel kan være i flere publikationer. Du bør tilføje objekter eksplicit til en publikation, undtagen hvis du vælger "ALLE TABELLER", som kræver et superbrugerprivilegium.
Du kan begrænse ændringerne af objekter (INSERT, UPDATE og DELETE), der skal replikeres. Som standard replikeres alle operationstyper. Du skal have en replikeringsidentitet konfigureret for det objekt, du vil føje til en publikation. Dette er for at replikere OPDATERING og SLET operationer. Replikeringsidentiteten kan være en primær nøgle eller et unikt indeks. Hvis tabellen ikke har en primær nøgle eller et unikt indeks, kan den indstilles til replika-identitet "fuld", hvori den tager alle kolonner som nøgle (hele rækken bliver nøgle).
Du kan oprette en publikation ved at bruge CREATE PUBLICATION. Nogle praktiske kommandoer er dækket i afsnittet "Sådan opnås det".
Abonnementet kan defineres på destinationsserveren, og noden, hvorpå det er defineret, omtales som "abonnenten". Forbindelsen til kildedatabasen er defineret i abonnementet. Abonnentnoden er den samme som enhver anden selvstændig postgres-database, og du kan også bruge den som en publikation til yderligere abonnementer.
Abonnementet tilføjes ved hjælp af CREATE SUBSCRIPTION og kan til enhver tid stoppes/genoptages ved hjælp af kommandoen ALTER SUBSCRIPTION og fjernes ved hjælp af DROP SUBSCRIPTION.
Når et abonnement er oprettet, kopierer logisk replikering et øjebliksbillede af dataene i udgiverdatabasen. Når det er gjort, venter den på deltaændringer og sender dem til abonnementsknuden, så snart de opstår.
Men hvordan indsamles ændringerne? Hvem sender dem til målet? Og hvem anvender dem på målet? Logisk replikering er også baseret på den samme arkitektur som fysisk replikering. Det implementeres af "walsender" og "anvend" processer. Da det er baseret på WAL-afkodning, hvem starter afkodningen? Walsender-processen er ansvarlig for at starte logisk afkodning af WAL og indlæser standard logisk afkodningsplugin (pgoutput). Pluginnet transformerer de læste ændringer fra WAL til den logiske replikeringsprotokol og filtrerer dataene i henhold til publikationsspecifikationen. Dataene overføres derefter kontinuerligt ved hjælp af streaming-replikeringsprotokollen til appliceringsarbejderen, som kortlægger dataene til lokale tabeller og anvender de individuelle ændringer, efterhånden som de modtages, i korrekt transaktionsrækkefølge.
Den logger alle disse trin i logfiler, mens den konfigureres. Vi kan se beskederne i afsnittet "Sådan opnår du det" senere i indlægget.
Funktioner ved logisk replikering
- Logisk replikering replikerer dataobjekter baseret på deres replikeringsidentitet (generelt en
- primær nøgle eller unikt indeks).
- Destinationsserver kan bruges til at skrive. Du kan have forskellige indekser og sikkerhedsdefinitioner.
- Logisk replikering har understøttelse af flere versioner. I modsætning til streamingreplikering kan logisk replikering indstilles mellem forskellige versioner af PostgreSQL (> 9.4 dog)
- Logisk replikering udfører hændelsesbaseret filtrering
- Sammenlignet har logisk replikering mindre skriveforstærkning end streaming replikering
- Publikationer kan have flere abonnementer
- Logisk replikering giver lagerfleksibilitet gennem replikering af mindre sæt (selv partitionerede tabeller)
- Minimum serverbelastning sammenlignet med triggerbaserede løsninger
- Tillader parallel streaming på tværs af udgivere
- Logisk replikering kan bruges til migreringer og opgraderinger
- Datatransformation kan udføres under opsætning.
Use Cases - Hvornår er logisk replikering nyttig?
Det er meget vigtigt at vide, hvornår man skal bruge logisk replikering. Ellers får du ikke meget udbytte, hvis din use case ikke stemmer overens. Så her er nogle eksempler på, hvornår du skal bruge logisk replikering:
- Hvis du ønsker at konsolidere flere databaser til en enkelt database til analytiske formål.
- Hvis dit krav er at replikere data mellem forskellige større versioner af PostgreSQL.
- Hvis du vil sende trinvise ændringer i en enkelt database eller en delmængde af en database til andre databaser.
- Hvis der gives adgang til replikerede data til forskellige grupper af brugere.
- Hvis du deler en delmængde af databasen mellem flere databaser.
Begrænsninger af logisk replikering
Logisk replikering har nogle begrænsninger, som fællesskabet løbende arbejder på for at overvinde:
- Tabeller skal have det samme fulde kvalificerede navn mellem udgivelse og abonnement.
- Tabeller skal have primær nøgle eller unik nøgle
- Gensidig (tovejs) replikering understøttes ikke
- Replikerer ikke skema/DDL
- Replikerer ikke sekvenser
- Replikerer ikke TRUNCATE
- Replikerer ikke store objekter
- Abonnementer kan have flere kolonner eller en anden rækkefølge af kolonner, men typerne og kolonnenavnene skal matche mellem udgivelse og abonnement.
- Superbrugerrettigheder til at tilføje alle tabeller
- Du kan ikke streame over til den samme vært (abonnementet bliver låst).
Sådan opnår du logisk replikering
Her er trinene til at opnå grundlæggende logisk replikering. Vi kan diskutere mere komplekse scenarier senere.
-
Initialiser to forskellige forekomster til udgivelse og abonnement og start.
C1MQV0FZDTY3:bin bajishaik$ export PATH=$PWD:$PATH C1MQV0FZDTY3:bin bajishaik$ which psql /Users/bajishaik/pg_software/10.2/bin/psql C1MQV0FZDTY3:bin bajishaik$ ./initdb -D /tmp/publication_db C1MQV0FZDTY3:bin bajishaik$ ./initdb -D /tmp/subscription_db
-
Parametre, der skal ændres, før du starter forekomsterne (for både udgivelses- og abonnementsforekomster).
C1MQV0FZDTY3:bin bajishaik$ tail -3 /tmp/publication_db/postgresql.conf listen_addresses='*' port = 5555 wal_level= logical C1MQV0FZDTY3:bin bajishaik$ pg_ctl -D /tmp/publication_db/ start waiting for server to start....2018-03-21 16:03:30.394 IST [24344] LOG: listening on IPv4 address "0.0.0.0", port 5555 2018-03-21 16:03:30.395 IST [24344] LOG: listening on IPv6 address "::", port 5555 2018-03-21 16:03:30.544 IST [24344] LOG: listening on Unix socket "/tmp/.s.PGSQL.5555" 2018-03-21 16:03:30.662 IST [24345] LOG: database system was shut down at 2018-03-21 16:03:27 IST 2018-03-21 16:03:30.677 IST [24344] LOG: database system is ready to accept connections done server started C1MQV0FZDTY3:bin bajishaik$ tail -3 /tmp/subscription_db/postgresql.conf listen_addresses='*' port=5556 wal_level=logical C1MQV0FZDTY3:bin bajishaik$ pg_ctl -D /tmp/subscription_db/ start waiting for server to start....2018-03-21 16:05:28.408 IST [24387] LOG: listening on IPv4 address "0.0.0.0", port 5556 2018-03-21 16:05:28.408 IST [24387] LOG: listening on IPv6 address "::", port 5556 2018-03-21 16:05:28.410 IST [24387] LOG: listening on Unix socket "/tmp/.s.PGSQL.5556" 2018-03-21 16:05:28.460 IST [24388] LOG: database system was shut down at 2018-03-21 15:59:32 IST 2018-03-21 16:05:28.512 IST [24387] LOG: database system is ready to accept connections done server started
Andre parametre kan være som standard for grundlæggende opsætning.
-
Skift filen pg_hba.conf for at tillade replikering. Bemærk, at disse værdier afhænger af dit miljø, men dette er kun et grundlæggende eksempel (for både udgivelses- og abonnementsforekomster).
C1MQV0FZDTY3:bin bajishaik$ tail -1 /tmp/publication_db/pg_hba.conf host all repuser 0.0.0.0/0 md5 C1MQV0FZDTY3:bin bajishaik$ tail -1 /tmp/subscription_db/pg_hba.conf host all repuser 0.0.0.0/0 md5 C1MQV0FZDTY3:bin bajishaik$ psql -p 5555 -U bajishaik -c "select pg_reload_conf()" Timing is on. Pager usage is off. 2018-03-21 16:08:19.271 IST [24344] LOG: received SIGHUP, reloading configuration files pg_reload_conf ---------------- t (1 row) Time: 16.103 ms C1MQV0FZDTY3:bin bajishaik$ psql -p 5556 -U bajishaik -c "select pg_reload_conf()" Timing is on. Pager usage is off. 2018-03-21 16:08:29.929 IST [24387] LOG: received SIGHUP, reloading configuration files pg_reload_conf ---------------- t (1 row) Time: 53.542 ms C1MQV0FZDTY3:bin bajishaik$
-
Opret et par testtabeller for at replikere og indsæt nogle data på publikationsforekomsten.
postgres=# create database source_rep; CREATE DATABASE Time: 662.342 ms postgres=# \c source_rep You are now connected to database "source_rep" as user "bajishaik". source_rep=# create table test_rep(id int primary key, name varchar); CREATE TABLE Time: 63.706 ms source_rep=# create table test_rep_other(id int primary key, name varchar); CREATE TABLE Time: 65.187 ms source_rep=# insert into test_rep values(generate_series(1,100),'data'||generate_series(1,100)); INSERT 0 100 Time: 2.679 ms source_rep=# insert into test_rep_other values(generate_series(1,100),'data'||generate_series(1,100)); INSERT 0 100 Time: 1.848 ms source_rep=# select count(1) from test_rep; count ------- 100 (1 row) Time: 0.513 ms source_rep=# select count(1) from test_rep_other ; count ------- 100 (1 row) Time: 0.488 ms source_rep=#
-
Opret struktur af tabellerne på Subscription-instansen, da logisk replikering ikke replikerer strukturen.
postgres=# create database target_rep; CREATE DATABASE Time: 514.308 ms postgres=# \c target_rep You are now connected to database "target_rep" as user "bajishaik". target_rep=# create table test_rep_other(id int primary key, name varchar); CREATE TABLE Time: 9.684 ms target_rep=# create table test_rep(id int primary key, name varchar); CREATE TABLE Time: 5.374 ms target_rep=#
-
Opret publikation på publikationsinstans (port 5555).
source_rep=# CREATE PUBLICATION mypub FOR TABLE test_rep, test_rep_other; CREATE PUBLICATION Time: 3.840 ms source_rep=#
-
Opret abonnement på Subscription-instansen (port 5556) til den publikation, der blev oprettet i trin 6.
target_rep=# CREATE SUBSCRIPTION mysub CONNECTION 'dbname=source_rep host=localhost user=bajishaik port=5555' PUBLICATION mypub; NOTICE: created replication slot "mysub" on publisher CREATE SUBSCRIPTION Time: 81.729 ms
Fra log:
2018-03-21 16:16:42.200 IST [24617] LOG: logical decoding found consistent point at 0/1616D80 2018-03-21 16:16:42.200 IST [24617] DETAIL: There are no running transactions. target_rep=# 2018-03-21 16:16:42.207 IST [24618] LOG: logical replication apply worker for subscription "mysub" has started 2018-03-21 16:16:42.217 IST [24619] LOG: starting logical decoding for slot "mysub" 2018-03-21 16:16:42.217 IST [24619] DETAIL: streaming transactions committing after 0/1616DB8, reading WAL from 0/1616D80 2018-03-21 16:16:42.217 IST [24619] LOG: logical decoding found consistent point at 0/1616D80 2018-03-21 16:16:42.217 IST [24619] DETAIL: There are no running transactions. 2018-03-21 16:16:42.219 IST [24620] LOG: logical replication table synchronization worker for subscription "mysub", table "test_rep" has started 2018-03-21 16:16:42.231 IST [24622] LOG: logical replication table synchronization worker for subscription "mysub", table "test_rep_other" has started 2018-03-21 16:16:42.260 IST [24621] LOG: logical decoding found consistent point at 0/1616DB8 2018-03-21 16:16:42.260 IST [24621] DETAIL: There are no running transactions. 2018-03-21 16:16:42.267 IST [24623] LOG: logical decoding found consistent point at 0/1616DF0 2018-03-21 16:16:42.267 IST [24623] DETAIL: There are no running transactions. 2018-03-21 16:16:42.304 IST [24621] LOG: starting logical decoding for slot "mysub_16403_sync_16393" 2018-03-21 16:16:42.304 IST [24621] DETAIL: streaming transactions committing after 0/1616DF0, reading WAL from 0/1616DB8 2018-03-21 16:16:42.304 IST [24621] LOG: logical decoding found consistent point at 0/1616DB8 2018-03-21 16:16:42.304 IST [24621] DETAIL: There are no running transactions. 2018-03-21 16:16:42.306 IST [24620] LOG: logical replication table synchronization worker for subscription "mysub", table "test_rep" has finished 2018-03-21 16:16:42.308 IST [24622] LOG: logical replication table synchronization worker for subscription "mysub", table "test_rep_other" has finished
Som du kan se i NOTICE-meddelelsen, oprettede den en replikeringsplads, som sikrer, at WAL-oprydningen ikke bør udføres, før indledende snapshot- eller deltaændringer er overført til måldatabasen. Derefter begyndte WAL-afsenderen at afkode ændringerne, og logisk replikering virkede, da både pub og sub startes. Så starter den tabelsynkroniseringen.
-
Bekræft data på abonnementsforekomst.
target_rep=# select count(1) from test_rep; count ------- 100 (1 row) Time: 0.927 ms target_rep=# select count(1) from test_rep_other ; count ------- 100 (1 row) Time: 0.767 ms target_rep=#
Som du kan se, er data blevet replikeret gennem indledende snapshot.
-
Bekræft deltaændringer.
C1MQV0FZDTY3:bin bajishaik$ psql -d postgres -p 5555 -d source_rep -c "insert into test_rep values(generate_series(101,200), 'data'||generate_series(101,200))" INSERT 0 100 Time: 3.869 ms C1MQV0FZDTY3:bin bajishaik$ psql -d postgres -p 5555 -d source_rep -c "insert into test_rep_other values(generate_series(101,200), 'data'||generate_series(101,200))" INSERT 0 100 Time: 3.211 ms C1MQV0FZDTY3:bin bajishaik$ psql -d postgres -p 5556 -d target_rep -c "select count(1) from test_rep" count ------- 200 (1 row) Time: 1.742 ms C1MQV0FZDTY3:bin bajishaik$ psql -d postgres -p 5556 -d target_rep -c "select count(1) from test_rep_other" count ------- 200 (1 row) Time: 1.480 ms C1MQV0FZDTY3:bin bajishaik$
Dette er trinene til en grundlæggende opsætning af logisk replikering.