I det här blogginlägget kommer vi att prata om logisk replikering i PostgreSQL:dess användningsfall, allmän information om denna tekniks status och ett speciellt användningsfall i synnerhet om hur man ställer in en abonnentnod (replika) för den primära servern i ordning att fungera som databasserver för testmiljön, och de utmaningar som möttes.
Intro
Logisk replikering, officiellt introducerad i PostgreSQL 10, är den senaste replikeringstekniken som erbjuds av PostgreSQL-communityt. Logisk replikering är en fortsättning på arvet från fysisk replikering som den delar många idéer och kod med. Logisk replikering fungerar som fysisk replikering med hjälp av WAL för att registrera logiska förändringar oberoende av version eller specifik arkitektur. För att kunna tillhandahålla logisk replikering till kärnerbjudandet har PostgreSQL-communityt kommit långt.
Typer av replikering och historik för PostgreSQL-replikering
Typerna av replikering i databaser kan klassificeras enligt följande:
- Fysisk (AKA binär) replikering
- Operativsystemnivå (vSphere-replikering)
- Filsystemnivå (DRBD)
- Databasnivå (WAL-baserad)
- Logisk replikering (databasnivå)
- Triggerbaserad (DBMirror, Slony)
- Mellanprogram (pgpool)
- WAL-baserad (pglogical, logisk replikering)
Färdkartan som leder till dagens WAL-baserade logiska replikering var:
- 2001:DBMirror (triggerbaserad)
- 2004:Slony1 (triggerbaserad), pgpool (mellanprogram)
- 2005:PITR (WAL-baserad) introducerad i PostgreSQL 8.0
- 2006:Varm standby i PostgreSQL 8.2
- 2010:Fysisk strömmande replikering, hot standby i PostgreSQL 9.0
- 2011:Synkron strömmande replikering i PostgreSQL 9.1
- 2012:Cascading streaming replikering i PostgreSQL 9.2
- 2013:Bakgrundsarbetare i PostgreSQL 9.3
- 2014:API för logisk avkodning, replikeringsplatser. (Grunden för logisk replikering) i PostgreSQL 9.4
- 2015:2ndQuadrant introducerar pglogical, förfadern eller logisk replikering
- 2017:Logisk replikering i kärnan i PostgreSQL 10!
Som vi kan se har många tekniker samarbetat för att göra logisk replikering till verklighet:WAL-arkivering, varm/varm standby, fysisk WAL-replikering, bakgrundsarbetare, logisk avkodning. Förutsatt att läsaren är bekant med de flesta begreppen fysisk replikering, kommer vi att prata om de grundläggande komponenterna i logisk replikering.
PostgreSQL logisk replikering grundläggande koncept
Lite terminologi:
- Publikation: En uppsättning ändringar från en uppsättning tabeller definierade i en specifik databas på en primär replikeringsserver. En publikation kan hantera alla eller några av:INSERT, DELETE, UPDATE, TRUNCATE.
- Utgivarnod: Servern där publikationen finns.
- Replika identitet: Ett sätt att identifiera raden på abonnentsidan för UPPDATERINGAR och DELETE.
- Prenumeration: En koppling till en förlagsnod och en eller flera publikationer i den. En prenumeration använder en dedikerad replikeringsplats på utgivaren för replikering. Ytterligare replikeringsplatser kan användas för det inledande synkroniseringssteget.
- Prenumerantnod: Servern där prenumerationen finns.
Logisk replikering följer en publicerings-/prenumerationsmodell. En eller flera prenumeranter kan prenumerera på en eller flera publikationer på en förlagsnod. Prenumeranter kan återpublicera för att möjliggöra kaskadreplikering. Logisk replikering av en tabell består av två steg:
- Ta en ögonblicksbild av tabellen på utgivaren och kopiera den till prenumeranten
- Tillämpa alla ändringar (sedan ögonblicksbilden) i samma sekvens
Logisk replikering är transaktionell och garanterar att ordningen för ändringar som tillämpas på abonnenten förblir densamma som på utgivaren. Logisk replikering ger mycket mer frihet än fysisk (binär) replikering och kan därför användas på fler sätt:
- Enskild databas eller tabellspecifik replikering (du behöver inte replikera hela klustret)
- Ställa in utlösare för prenumeranten för en specifik uppgift (som anonymisering, vilket är ett ganska hett ämne efter att GDPR trädde i kraft)
- Att låta en prenumerantnod samla in data från många utgivarnoder, vilket möjliggör central analytisk bearbetning
- Replikering mellan olika versioner/arkitekturer/plattformar (noll uppgraderingar av driftstopp)
- Använda abonnentnoden som databasserver för en test-/utvecklingsmiljö. Varför vi vill ha detta beror på att testning mot verklig data är den mest realistiska typen av tester.
Varningar och begränsningar
Det finns vissa saker vi måste ha i åtanke när vi använder logisk replikering, vissa av dem kan påverka vissa designbeslut men andra kan leda till kritiska incidenter.
Begränsningar
- Endast DML-operationer stöds. Ingen DDL. Schemat måste definieras i förväg
- Sekvenser replikeras inte
- Stora objekt replikeras inte
- Endast vanliga bastabeller stöds (materialiserade vyer, partitionsrottabeller, främmande tabeller stöds inte)
Varningar
Det grundläggande problemet som vi förr eller senare måste möta när vi använder logisk replikering är konflikter på abonnenten. Prenumeranten är en normal läs/skrivserver som kan fungera som primär i en fysisk replikeringsuppsättning, eller till och med som utgivare i en kaskadlogisk replikeringsuppsättning. Så länge skrivningar på de prenumererade tabellerna utförs kan det finnas konflikter . En konflikt uppstår när replikerad data bryter mot en begränsning på tabellen de tillämpas på. Vanligtvis är operationen som orsakar detta INSERT, DELETES eller UPDATES som inte har någon effekt på grund av att rader saknas, vilket inte orsakar någon konflikt. När en konflikt uppstår upphör replikeringen. Den logiska bakgrundsarbetaren kommer att startas om i det angivna intervallet (wal_retrieve_retry_interval), men replikeringen kommer att misslyckas igen tills orsaken till konflikten är löst. Detta är ett kritiskt tillstånd som måste åtgärdas omedelbart. Om du inte gör det kommer replikeringsplatsen att fastna vid sin nuvarande position kommer utgivarnoden att börja ackumulera WAL och oundvikligen kommer utgivarnoden att få slut på diskutrymme . En konflikt är den vanligaste orsaken till att replikeringen kan stoppa men alla andra felaktiga tillstånd kommer att ha samma effekt:t.ex. vi lade till en ny NOT NULL-kolumn på en prenumererad tabell men glömde att definiera ett standardvärde, eller la till en kolumn på en publicerad tabell men glömde att definiera den på den prenumererade tabellen, eller gjorde ett misstag i dess typ och de två typerna är inte kompatibel. Alla dessa fel kommer att stoppa replikeringen. Det finns två sätt att lösa en konflikt:
- Lös det faktiska problemet
- Hoppa över den misslyckade transaktionen genom att anropa pg_replication_origin_advance
Lösning b. som också visas här kan vara farligt och knepigt eftersom det i grunden är en trial and error process, och om man väljer det aktuella LSN på utgivaren kan han/hon lätt få ett trasigt replikeringssystem eftersom det kan finnas operationer mellan det problematiska LSN:et och det nuvarande LSN som vi skulle vilja behålla. Så det bästa sättet är att faktiskt lösa problemet på abonnentsidan. T.ex. om en UNIQUE KEY-överträdelse uppstår kan vi uppdatera uppgifterna om abonnenten eller bara ta bort raden. I en produktionsmiljö måste allt detta vara automatiserat eller åtminstone halvautomatiserat.
Konfigurera utgivaren och prenumerantnoderna
För en allmän översikt över den logiska replikeringen i praktiken, läs den här bloggen.
De relevanta parametrarna för logisk replikering är:
- Utgivarsidan
- wal_level>="logisk"
- max_replication_slots>=#prenumerationer + initial tabellsynkronisering
- max_wal_senders>=max_replication_slots + other_physical_standbys
- Prenumerantsidan
- max_replication_slots>=#prenumerationer
- max_logical_replication_workers>=#prenumerationer + initial tabellsynkronisering
- max_worker_processes>=max_logical_replication_workers + 1 + max_parallel_workers
Vi kommer att fokusera på de speciella överväganden som följer av vårt speciella syfte som vi behöver logisk replikering för att uppnå:skapa ett testdatabaskluster för användning av testavdelningen . Publikationen kan definieras antingen för alla tabeller eller tabell för tabell. Jag föreslår tabell för tabell-metoden eftersom den ger oss maximal flexibilitet. De allmänna stegen kan sammanfattas enligt följande:
- Utför en ny initdb på prenumerantnoden
- Dumpa schemat för utgivarklustret och kopiera till prenumerantnoden
- Skapa schemat för prenumeranten
- Bestämma vilka bord du behöver och vilka du inte behöver.
När det gäller punkten ovan finns det två anledningar till varför du kanske inte behöver en tabell för att replikeras eller konfigureras för replikering:
- Det är ett dummybord utan betydelse (och du kanske borde ta bort det från produktionen också)
- är en tabell som är lokal för produktionsmiljön, vilket betyder att det är helt logiskt att samma tabell i testmiljön (abonnent) har sin egen data
Alla tabeller som deltar i den logiska replikeringen måste ha en REPLIKIDENTITET. Detta är som standard PRIMÄRNYCKEL, och om den inte är tillgänglig kan en UNIK nyckel definieras. Nästa steg för att hitta status för tabellerna med avseende på REPLICA IDENTITY.
- Hitta tabellerna utan någon uppenbar kandidat för REPLICA IDENTITY
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') ;
- Hitta tabellerna utan PRIMÄRNYCKEL men med ett UNIKT INDEX
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';
- Gå igenom listorna ovan och bestäm vad du ska göra med varje tabell
- Skapa publikationen med tabellerna för vilka det finns en 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
- Skapa sedan prenumerationen på prenumerantnoden
Ovanstående kommer också att kopiera data.create subscription data_for_testdb_pub CONNECTION 'dbname=yourdb host=yourdbhost user=repmgr' PUBLICATION data_for_testdb_pub ;
- Lägg till tabeller som du vill ha som har ett UNIKT index
Kör både i publicist- och prenumerantnoder, t.ex.:
På utgivaren:ALTER TABLE someschema.yourtable REPLICA IDENTITY USING INDEX yourindex_ukey;
På prenumeranten:ALTER PUBLICATION data_for_testdb_pub ADD TABLE ONLY someschema.yourtable;
ALTER SUBSCRIPTION data_for_testdb_pub REFRESH PUBLICATION WITH ( COPY_DATA );
- Vid denna punkt (synkronisering) bör du alltid ha ett öga på PostgreSQL-loggen på abonnentnoden. Du vill inte ha några fel eller något (timeout) som förbjuder fortsatt logisk replikering. LÖS EVENTUELLT FEL OMEDELBART , eller så kommer utgivaren att fortsätta samla WAL-filer i pg_wal och så småningom få ont om utrymme. Så du måste ta itu med
- Alla FEL eller något meddelande om den logiska arbetaren som leder till att den avslutas
- Ta även hand om
- wal_receiver_timeout
- wal_sender_timeout
Efter att ha löst alla problem bör du ha din abonnentnod igång. Så nästa fråga är hur man använder detta som en testdatabasserver. Du kommer att behöva ta itu med dessa problem/problem:
- Anonymisering
- Primära nycklar och unika nycklar som är baserade på sekvensöverträdelser
- En allmän uppsättning god praxis
- Övervakning
Anonymisering
När det gäller anonymisering av personuppgifter som upprätthålls av GDPR i EU bör du ALLTID skriva några triggers som tömmer ut alla fält gällande adresser, bankkonton, civilstånd, telefonnummer, e-post, etc. Du bör rådfråga din säkerhetsansvarige i ditt företag om vad man ska behålla och vad man ska ta bort. Utlösare bör definieras som ALLTID eftersom den logiska arbetaren kör satserna som REPLICA.
Primära nycklar med sekvenser
När det gäller sekvenser kommer det helt klart att bli ett problem med dessa nycklar om de inte åtgärdas innan något test påbörjas. Tänk på det här fallet:
- På fredag eftermiddag gör du några tester på prenumerantdatabasen och infogar en ny rad i någon tabell. Detta kommer att ha som ett ID nästa värde som genereras av sekvensen.
- Du åker hem över helgen.
- Någon produktionsanvändare anger en rad i samma tabell i utgivardatabasen.
- Raden kommer att replikeras baserat på REPLICA IDENTITY till abonnentnoden men kommer att misslyckas på grund av PK-överträdelse ERROR. Den logiska bakgrundsarbetaren avslutar och försöker igen. Men kommer att fortsätta misslyckas så länge problemet kvarstår.
- Replikeringen kommer att fastna. Replikeringsplatsen kommer att börja ackumulera WALs.
- Utgivaren får slut på diskutrymme.
- På helgen får du ett e-postmeddelande om att din primära nod har PANIK!
Så för att lösa sekvensproblemet kan du ta följande tillvägagångssätt:
select 'SELECT setval(''' || seqrelid::regclass||''','||CASE WHEN seqincrement <0 THEN -214748364 ELSE 214748364 END||');' from pg_sequence where seqtypid=20;
\gexec
Vad ovanstående gör är att ställa in sekvenser till ett tillräckligt stort värde så att de aldrig överlappar varandra för ett ganska stort fönster i framtiden, vilket gör att du kan ha en problemfri testserver.
En uppsättning god praxis
Du bör verkligen berätta för dina programmerare att göra sina tester icke-beständiga. Så alla tester efter att de är klara bör lämna databasen i samma tillstånd som den var före testet. Med sekvensbaserade ID-insättningar är detta inte ett problem, vi såg tidigare en lösning. Men med icke-sekvens (t.ex. sammansatta) UNIKA nycklar kan det vara ett problem. Så det är bäst att ta bort dessa testdata innan någon produktionsrad med samma värde träffar den prenumererade tabellen.
Här bör vi också lägga till hantering av schemaändringar. Alla schemaändringar måste också göras på abonnenten för att inte bryta replikerad DML-trafik.
Ladda ner Whitepaper Today PostgreSQL Management &Automation med ClusterControlLäs om vad du behöver veta för att distribuera, övervaka, hantera och skala PostgreSQLDladda WhitepaperÖvervakning
Du borde verkligen investera i en bra övervakningslösning. Du bör övervaka för ...
Hos abonnenten:
- ALLA meddelanden i abonnentens logg som är relevanta för logisk arbetaravslutning. Att installera ett verktyg som tail_n_mail kan verkligen hjälpa till med detta. En konfiguration som är känd för att fungera:
När vi får en varning från tail_n_mail bör vi omedelbart lösa problemet.INCLUDE: ERROR: .*publisher.* INCLUDE: ERROR: .*exited with exit.* INCLUDE: LOG: .*exited with exit.* INCLUDE: FATAL: INCLUDE: PANIC:
- pg_stat_subscription. Pid bör inte vara null. Fördröjningen bör också vara liten.
Hos förlaget:
- pg_stat_replikering. Detta bör ha så många rader som de är tänkta att vara:En för varje ansluten strömmande replikeringsberedskap (abonnentnoder och andra fysiska beredskapslägen ingår).
- pg_replication_slots för abonnentplatsen. Detta bör vara aktivt.
I allmänhet tar det lite tid innan du har din idealiska testdatabasserver igång utan problem, men när du har löst dem alla kommer dina programmerare att tacka dig för att du har den!