sql >> Databasteknik >  >> RDS >> MariaDB

Hantera replikeringsproblem från icke-GTID till GTID MariaDB-databaskluster

Vi stötte nyligen på ett intressant kundsupportärende som involverade en replikeringsinstallation av MariaDB. Vi ägnade mycket tid åt att undersöka det här problemet och tyckte att det skulle vara värt att dela detta med dig i det här blogginlägget.

Kundens miljöbeskrivning

Problemet var följande:en gammal (före 10.x) MariaDB-server användes och ett försök gjordes att migrera data från den till nyare MariaDB-replikeringsinställningar. Detta resulterade i problem med att använda Mariabackup för att bygga om slavar i det nya replikeringsklustret. I syftet med testerna återskapade vi detta beteende i följande miljö:

Datan har migrerats från 5.5 till 10.4 med mysqldump:

mysqldump --single-transaction --master-data=2 --events --routines sbtest > /root/dump.sql

Detta gjorde det möjligt för oss att samla in huvudbinära loggkoordinater och den konsekventa dumpen. Som ett resultat kunde vi tillhandahålla MariaDB 10.4 huvudnod och ställa in replikeringen mellan gammal 5.5 master och ny 10.4 nod. Trafiken pågick fortfarande på noden 5,5. 10.4 master genererade GTID eftersom den var tvungen att replikera data till 10.4 slav. Innan vi gräver i detaljer, låt oss ta en snabb titt på hur GTID fungerar i MariaDB.

MariaDB och GTID

Till att börja med använder MariaDB ett annat format för GTID än Oracle MySQL. Den består av tre siffror separerade med bindestreck:

0 - 1 - 345

Först är en replikeringsdomän som gör att replikering med flera källor kan hanteras korrekt. Detta är inte relevant för vårt fall eftersom alla noder är i samma replikeringsdomän. Andra numret är server-ID:t för noden som genererade GTID. Den tredje är sekvensnumret - det ökar monotont för varje händelse som lagras i de binära loggarna.

MariaDB använder flera variabler för att lagra information om GTID:er som körs på en given nod. De mest intressanta för oss är:

Gtid_binlog_pos - enligt dokumentationen är denna variabel GTID för den senaste händelsegruppen som skrevs till den binära loggen.

Gtid_slave_pos - enligt dokumentationen innehåller denna systemvariabel GTID för den senaste transaktionen som tillämpades på databasen av serverns slavtrådar.

Gtid_current_pos - enligt dokumentationen innehåller denna systemvariabel GTID för den senaste transaktionen som tillämpades på databasen. Om server_id för motsvarande GTID i gtid_binlog_pos är lika med serverns eget server_id, och sekvensnumret är högre än motsvarande GTID i gtid_slave_pos, kommer GTID från gtid_binlog_pos att användas. Annars kommer GTID från gtid_slave_pos att användas för den domänen.

Så, för att göra det tydligt, lagrar gtid_binlog_pos GTID för den senaste lokalt körda händelsen. Gtid_slave_pos lagrar GTID för händelsen som exekveras av slavtråden och gtid_current_pos visar antingen värdet från gtid_binlog_pos, om den har det högsta sekvensnumret och den har server-id eller gtid_slave_pos om den har den högsta sekvensen. Ha detta i ditt sinne.

En översikt över problemet

Initialtillståndet för de relevanta variablerna är på 10.4 master:

MariaDB [(none)]> show global variables like '%gtid%';

+-------------------------+----------+

| Variable_name           | Value |

+-------------------------+----------+

| gtid_binlog_pos         | 0-1001-1 |

| gtid_binlog_state       | 0-1001-1 |

| gtid_cleanup_batch_size | 64       |

| gtid_current_pos        | 0-1001-1 |

| gtid_domain_id          | 0 |

| gtid_ignore_duplicates  | ON |

| gtid_pos_auto_engines   | |

| gtid_slave_pos          | 0-1001-1 |

| gtid_strict_mode        | ON |

| wsrep_gtid_domain_id    | 0 |

| wsrep_gtid_mode         | OFF |

+-------------------------+----------+

11 rows in set (0.001 sec)

Observera gtid_slave_pos som teoretiskt sett inte är vettigt - det kom från samma nod men via slavtråden. Detta kan hända om du gör ett huvudbyte tidigare. Vi gjorde just det - med två 10.4-noder bytte vi masterna från värd med server-ID 1001 till värd med server-ID 1002 och sedan tillbaka till 1001.

Efteråt konfigurerade vi replikeringen från 5.5 till 10.4 och så här såg det ut:

MariaDB [(none)]> show global variables like '%gtid%';

+-------------------------+-------------------------+

| Variable_name           | Value |

+-------------------------+-------------------------+

| gtid_binlog_pos         | 0-55-117029 |

| gtid_binlog_state       | 0-1001-1537,0-55-117029 |

| gtid_cleanup_batch_size | 64                      |

| gtid_current_pos        | 0-1001-1 |

| gtid_domain_id          | 0 |

| gtid_ignore_duplicates  | ON |

| gtid_pos_auto_engines   | |

| gtid_slave_pos          | 0-1001-1 |

| gtid_strict_mode        | ON |

| wsrep_gtid_domain_id    | 0 |

| wsrep_gtid_mode         | OFF |

+-------------------------+-------------------------+

11 rows in set (0.000 sec)

Som du kan se, händelserna som replikeras från MariaDB 5.5, har alla redovisats i gtid_binlog_pos-variabeln:alla händelser med server-ID 55. Detta resulterar i ett allvarligt problem. Som du kanske minns bör gtid_binlog_pos innehålla händelser som körs lokalt på värden. Här innehåller den händelser replikerade från en annan server med ett annat server-ID.

Detta gör saker svåra när du vill bygga om 10.4-slaven, här är anledningen. Mariabackup fungerar precis som Xtrabackup på ett enkelt sätt. Den kopierar filerna från MariaDB-servern samtidigt som den skannar redo-loggar och lagrar alla inkommande transaktioner. När filerna har kopierats skulle Mariabackup frysa databasen med antingen SPOLA TABELLER MED LÄSSLÅS eller säkerhetskopieringslås, beroende på MariaDB-versionen och tillgängligheten för säkerhetskopieringslåsen. Sedan läser den det senast körda GTID och lagrar det tillsammans med säkerhetskopian. Sedan släpps låset och säkerhetskopieringen är klar. GTID som lagras i säkerhetskopian ska användas som det senast körda GTID på en nod. Vid återuppbyggnad av slavar kommer den att läggas som en gtid_slave_pos och sedan användas för att starta GTID-replikeringen. Detta GTID är hämtat från gtid_current_pos, vilket är helt logiskt - trots allt är det "GTID för den senaste transaktionen som tillämpades på databasen". Akut läsare kan redan se problemet. Låt oss visa utdata från variablerna när 10.4 replikerar från 5.5 master:

MariaDB [(none)]> show global variables like '%gtid%';

+-------------------------+-------------------------+

| Variable_name           | Value |

+-------------------------+-------------------------+

| gtid_binlog_pos         | 0-55-117029 |

| gtid_binlog_state       | 0-1001-1537,0-55-117029 |

| gtid_cleanup_batch_size | 64                      |

| gtid_current_pos        | 0-1001-1 |

| gtid_domain_id          | 0 |

| gtid_ignore_duplicates  | ON |

| gtid_pos_auto_engines   | |

| gtid_slave_pos          | 0-1001-1 |

| gtid_strict_mode        | ON |

| wsrep_gtid_domain_id    | 0 |

| wsrep_gtid_mode         | OFF |

+-------------------------+-------------------------+

11 rows in set (0.000 sec)

Gtid_current_pos är inställd på 0-1001-1. Det här är definitivt inte rätt ögonblick, det är hämtat från gtid_slave_pos medan vi har ett gäng transaktioner som kom från 5.5 efter det. Problemet är att dessa transaktioner lagras som gtid_binlog_pos. Å andra sidan beräknas gtid_current_pos på ett sätt att det kräver lokalt server-ID för GTID i gitd_binlog_pos innan de kan användas som gtid_current_pos. I vårt fall har de server-ID för 5.5-noden så de kommer inte att behandlas korrekt som händelser som exekveras på 10.4-mastern. Efter säkerhetskopieringsåterställning, om du skulle ställa in slaven enligt GTID-tillståndet som lagrats i säkerhetskopian, skulle det sluta med att återanvända alla händelser som kom från 5.5. Detta skulle uppenbarligen bryta replikeringen.

Lösningen

En lösning på detta problem är att ta flera ytterligare steg:

  1. Stoppa replikeringen från 5.5 till 10.4. Kör STOP SLAVE på 10.4 master
  2. Utför alla transaktioner den 10.4 - SKAPA SCHEMA OM INTE FINNS buggfix - detta kommer att ändra GTID-situationen så här:
MariaDB [(none)]> show global variables like '%gtid%';

+-------------------------+---------------------------+

| Variable_name           | Value   |

+-------------------------+---------------------------+

| gtid_binlog_pos         | 0-1001-117122   |

| gtid_binlog_state       | 0-55-117121,0-1001-117122 |

| gtid_cleanup_batch_size | 64                        |

| gtid_current_pos        | 0-1001-117122   |

| gtid_domain_id          | 0   |

| gtid_ignore_duplicates  | ON   |

| gtid_pos_auto_engines   |   |

| gtid_slave_pos          | 0-1001-1   |

| gtid_strict_mode        | ON   |

| wsrep_gtid_domain_id    | 0   |

| wsrep_gtid_mode         | OFF   |

+-------------------------+---------------------------+

11 rows in set (0.001 sec)

Den senaste GITD exekverades lokalt, så den lagrades som gtid_binlog_pos. Eftersom den har det lokala server-ID:t väljs den som gtid_current_pos. Nu kan du ta en säkerhetskopia och använda den för att bygga om slavar från 10.4 master. När detta är gjort, starta slavtråden igen.

MariaDB är medveten om att den här typen av buggar finns, en av de relevanta felrapporterna vi hittade är: https://jira.mariadb.org/browse/MDEV-10279 Tyvärr finns det ingen åtgärd än så länge . Vad vi hittade är att det här problemet påverkar MariaDB upp till 5.5. Icke-GTID-händelser som kommer från MariaDB 10.0 redovisas korrekt den 10.4 som kommer från slavtråden och gtid_slave_pos är korrekt uppdaterad. MariaDB 5.5 är ganska gammal (även om den fortfarande stöds) så du kan fortfarande se inställningar som körs på den och försök att migrera från 5.5 till nyare, GTID-aktiverade MariaDB-versioner. Vad värre är, enligt buggrapporten vi hittade, påverkar detta också replikering som kommer från icke-MariaDB (en av kommentarerna nämner problem som dyker upp på Percona Server 5.6) servrar till MariaDB.

I alla fall, vi hoppas att du tyckte att det här blogginlägget var användbart och förhoppningsvis kommer du inte att stöta på problemet vi just beskrev.


  1. Hur ändrar man teckenuppsättningen (och SAMMANSTÄLLNING) i en databas?

  2. MySQL - Få radnummer på select

  3. T-SQL SET Operatörer del 2:KORS och UTOM

  4. Dynamisk SQL (EXECUTE) som villkor för IF-satsen