MariaDB har introduceret en meget cool funktion kaldet Flashback. Flashback er en funktion, der gør det muligt at rulle instanser, databaser eller tabeller tilbage til et gammelt snapshot. For at udføre en punkt-i-tidsgendannelse (PITR) ville man traditionelt gendanne en database fra en sikkerhedskopi og afspille de binære logfiler for at rulle fremad i databasetilstanden på et bestemt tidspunkt eller en bestemt position.
Med Flashback kan databasen rulles tilbage til et tidspunkt i fortiden, hvilket er meget hurtigere, hvis vi bare vil se fortiden, der lige skete for ikke lang tid siden. Nogle gange kan det være ineffektivt at bruge flashback, hvis du vil se et meget gammelt øjebliksbillede af dine data i forhold til den aktuelle dato og klokkeslæt. Gendannelse fra en forsinket slave eller fra en backup plus genafspilning af den binære log kan være de bedre muligheder.
Denne funktion er kun tilgængelig i MariaDB-klientpakken, men det betyder ikke, at vi ikke kan bruge den med vores MySQL-servere. Dette blogindlæg viser, hvordan vi kan bruge denne fantastiske funktion på en MySQL-server.
Krav til MariaDB Flashback
For dem, der ønsker at bruge MariaDB flashback-funktionen oven på MySQL, kan vi grundlæggende gøre følgende:
- Aktiver binær log med følgende indstilling:
- binlog_format =ROW (standard siden MySQL 5.7.7).
- binlog_row_image =FULD (standard siden MySQL 5.6).
- Brug msqlbinlog-værktøjet fra enhver MariaDB 10.2.4 og senere installation.
- Flashback understøttes i øjeblikket kun over DML-sætninger (INSERT, DELETE, UPDATE). En kommende version af MariaDB vil tilføje understøttelse af flashback over DDL-sætninger (DROP, TRUNCATE, ALTER osv.) ved at kopiere eller flytte den aktuelle tabel til en reserveret og skjult database og derefter kopiere eller flytte tilbage, når du bruger flashback.
Flashback opnås ved at drage fordel af eksisterende understøttelse af binære logfiler i fuld billedformat, så det understøtter alle lagringsmotorer. Bemærk, at flashback-begivenhederne vil blive gemt i hukommelsen. Derfor bør du sikre dig, at din server har nok hukommelse til denne funktion.
Hvordan fungerer MariaDB Flashback?
MariaDBs mysqlbinlog-værktøj kommer med to ekstra muligheder til dette formål:
- -B, --flashback - Flashback-funktionen kan rulle dine forpligtede data tilbage til et særligt tidspunkt.
- -T, --table=[navn] - Listeposter for kun denne tabel (kun lokal log).
Ved at sammenligne mysqlbinlog-outputtet med og uden --flashback-flaget, kan vi nemt forstå, hvordan det virker. Overvej, at følgende sætning udføres på en MariaDB-server:
MariaDB> SLET FRA sbtest.sbtest1 WHERE id =1;
Uden flashback-flag vil vi se den faktiske DELETE binlog-begivenhed:
$ mysqlbinlog -vv \--start-datetime="$(date '+%F %T' -d 'nu - 10 minutter')" \--database=sbtest \--table=sbtest1 \/var/lib/mysql/binlog.000003...# at 453196541#200227 12:58:18 server id 37001 end_log_pos 453196766 CRC32 0xdaa248ed Delete_rows:table id 238 flags:STMT_END_FBINLOG '6rxXXhOJkAAAQwAAAP06AxsAAO4AAAAAAAEABnNidGVzdAAHc2J0ZXN0MQAEAwP+/gTu4P7wAAEBAAID/P8AFuAQfA==6rxXXiCJkAAA4QAAAN47AxsAAO4AAAAAAAEAAgAE/wABAAAAVJ4HAHcAODM4Njg2NDE5MTItMjg3NzM5NzI4MzctNjA3MzYxMjA0ODYtNzUxNjI2NTk5MDYtMjc1NjM1MjY0OTQtMjAzODE4ODc0MDQtNDE1NzY0MjIyNDEtOTM0MjY3OTM5NjQtNTY0MDUwNjUxMDItMzM1MTg0MzIzMzA7Njc4NDc5NjczNzctNDgwMDA5NjMzMjItNjI2MDQ3ODUzMDEtOTE0MTU0OTE4OTgtOTY5MjY1MjAyOTHtSKLa '/*!*/;### SLET FRA `sbtest`.`sbtest1`### WHERE### @1=1 /* INT meta=0 nullable=0 is_null=0 */### @2=499284 /* INT meta=0 nullable=0 is_null=0 */### @3='83868641912-28773972837-60736120486-75162659906-27563526494-20381887404-41576422241-93426793964-56405065102-33518432330' /* STRING(480) meta=61152 nullab le=0 is_null=0 */### @4='67847967377-48000963322-62604785301-91415491898-96926520291' /* STRING(240) meta=65264 *null-kode =65264 *null-kode
Ved at udvide ovenstående mysqlbinlog-kommando med --flashback, kan vi se DELETE-hændelsen konverteres til en INSERT-begivenhed og på samme måde til de respektive WHERE- og SET-klausuler:
$ mysqlbinlog -vv \--start-datetime="$(date '+%F %T' -d 'nu - 10 minutter')" \--database=sbtest \--table=sbtest1 \/var/lib/mysql/binlog.000003 \--flashback...BINLOG '6rxXXhOJkAAAQwAAAP06AxsAAO4AAAAAAAEABnNidGVzdAAHc2J0ZXN0MQAEAwP+/gTu4P7wAAEBAAID/P8AFuAQfA==6rxXXh6JkAAA4QAAAN47AxsAAO4AAAAAAAEAAgAE/wABAAAAVJ4HAHcAODM4Njg2NDE5MTItMjg3NzM5NzI4MzctNjA3MzYxMjA0ODYtNzUxNjI2NTk5MDYtMjc1NjM1MjY0OTQtMjAzODE4ODc0MDQtNDE1NzY0MjIyNDEtOTM0MjY3OTM5NjQtNTY0MDUwNjUxMDItMzM1MTg0MzIzMzA7Njc4NDc5NjczNzctNDgwMDA5NjMzMjItNjI2MDQ3ODUzMDEtOTE0MTU0OTE4OTgtOTY5MjY1MjAyOTHtSKLa'/*!*/;### INSERT INTO `sbtest`.`sbtest1`# ## SET### @1=1 /* INT meta=0 nullable=0 is_null=0 */### @2=499284 /* INT meta=0 nullable=0 is_null=0 */### @3 ='83868641912-28773972837-60736120486-75162659906-27563526494-20381887404-41576422241-93426793964-56405065102-33518432330' /* STRING(480) meta=61152 nullable=0 is_null=0 */### @4='67847967377-48000963322- 62604785301-91415491898-96926520291' /* STRING(240) meta=65264 nulla ble=0 is_null=0 */...
I rækkebaseret replikering (binlog_format=ROW) indeholder hver rækkeændringshændelse to billeder, et "før"-billede (undtagen INSERT), hvis kolonner matches mod, når der søges efter den række, der skal opdateres, og et "efter"-billede (undtagen DELETE), der indeholder ændringerne. Med binlog_row_image=FULD, logger MariaDB hele rækker (det vil sige alle kolonner) for både før- og efterbillederne.
Følgende eksempel viser binære loghændelser for UPDATE. Overvej, at følgende sætning udføres på en MariaDB-server:
MariaDB> OPDATERING sbtest.sbtest1 SET k =0 WHERE id =5;
Når vi ser på binlog-begivenheden for ovenstående erklæring, vil vi se noget som dette:
$ mysqlbinlog -vv \--start-datetime="$(date '+%F %T' -d 'nu - 5 minutter')" \--database=sbtest \--table=sbtest1 \/var/lib/mysql/binlog.000001 ...### OPDATERING `sbtest`.`sbtest1`### WHERE### @1=5 /* INT meta=0 nullable=0 is_null=0 */ ### @2=499813 /* INT meta=0 nullable=0 is_null=0 */### @3='44257470806-17967007152-32809666989-26174672567-29883439075-95767161284-94957565003-35708767253-53935174705-16168070783' /* STRING(480) meta=61152 nullable=0 is_null=0 */### @4='34551750492-67990399350-81179284955-79299808058-21257255869*0ll=a_STRING/64/069/64/01/64/01/480* ## SET### @1=5 /* INT meta=0 nullable=0 is_null=0 */### @2=0 /* INT meta=0 nullable=0 is_null=0 */### @3 ='44257470806-17967007152-32809666989-26174672567-29883439075-95767161284-94957565003-35708767253-53935174705-16168070783' /* STRING(480) meta=61152 nullable=0 is_null=0 */### @4='34551750492-67990399350- 81179284955-79299808058-21257255869' /* STRING(240) meta=65264 nullable=0 is_null=0 */# Antal rækker:1...
Med --flashback-flaget byttes "før"-billedet med "efter"-billedet af den eksisterende række:
$ mysqlbinlog -vv \--start-datetime="$(date '+%F %T' -d 'nu - 5 minutter')" \--database=sbtest \--table=sbtest1 \/var/lib/mysql/binlog.000001 \ --flashback...### OPDATERING `sbtest`.`sbtest1`### WHERE### @1=5 /* INT meta=0 nullable=0 is_null =0 */### @2 =omkring 16168070783' /* STRING(480) meta=61152 nullable=0 is_null=0 */### @4='34551750492-67990399350-81179284955-792998-208052 null=08118000/81179284955-79299-5086572/8299985080572/829998508052 null*02998-208052' 0 */### SET### @1=5 /* INT meta=0 nullable=0 is_null=0 */### @2=499813 /* INT meta=0 nullable=0 is_null=0 */# ## @3='44257470806-17967007152-32809666989-26174672567-29883439075-95767161284-94957565003-35708767253-53935174705-16168070783' /* STRING(480) meta=61152 nullable=0 is_null=0 */### @4=' 34551750492-67990399350-81179284955-79299808058-21257255869' /* STRING(240) meta=65264 nullable=0 i s_null=0 */...
Vi kan derefter omdirigere flashback-outputtet til MySQL-klienten og dermed rulle databasen eller tabellen tilbage til det tidspunkt, vi ønsker. Flere eksempler er vist i de næste afsnit.
MariaDB har en dedikeret videnbaseside til denne funktion. Tjek MariaDB Flashback-videnbasesiden.
MariaDB Flashback med MySQL
For at have flashback-evnen til MySQL skal man gøre følgende:
- Kopiér mysqlbinlog-værktøjet fra enhver MariaDB-server (10.2.4 eller nyere).
- Deaktiver MySQL GTID før du anvender flashback SQL-filen. Globale variabler gtid_mode og enforce_gtid_consistency kan indstilles i runtime siden MySQL 5.7.5.
Antag, at vi har følgende simple MySQL 8.0-replikeringstopologi:
I dette eksempel kopierede vi mysqlbinlog-værktøjet fra den seneste MariaDB 10.4 på en af vores MySQL 8.0-slaver (slave2):
(mariadb-server)$ scp /bin/mysqlbinlog [email protected]:/root/(slave2-mysql8)$ ls -l /root/mysqlbinlog-rwxr-xr-x. 1 root root 4259504 27. feb 13:44 /root/mysqlbinlog
Vores MariaDB's mysqlbinlog-værktøj er nu placeret på /root/mysqlbinlog på slave2. På MySQL-masteren udførte vi følgende katastrofale udtalelse:
mysql> SLET FRA sbtest1 WHERE id MELLEM 5 OG 100;Forespørgsel OK, 96 rækker påvirket (0,01 sek.)
96 rækker blev slettet i ovenstående opgørelse. Vent et par sekunder for at lade hændelserne replikere fra master til alle slaver, før vi kan forsøge at finde binlogpositionen for den katastrofale hændelse på slaveserveren. Det første trin er at hente alle de binære logfiler på den server:
mysql> VIS BINÆRE LOGS;+-------------------------- --+| Log_navn | Filstørrelse | Krypteret |+--------------------------+| binlog.000001 | 850 | Nej || binlog.000002 | 18796 | Nej |+---------------+------------+-------+
Vores katastrofale hændelse skulle eksistere inde i binlog.000002, den seneste binære log på denne server. Vi kan derefter bruge MariaDB's mysqlbinlog-værktøj til at hente alle binlog-begivenheder for tabel sbtest1 siden 10 minutter siden:
(slave2-mysql8)$ /root/mysqlbinlog -vv \--start-datetime="$(dato '+%F %T' -d 'nu - 10 minutter')" \--database =sbtest \--table=sbtest1 \/var/lib/mysql/binlog.000002...# at 195#200228 15:09:45 server-id 37001 end_log_pos 281 CRC32 0x99547474 Ignorable hændelsestype 3L3tid# Ignorable# QL3tid på 281#200228 15:09:45 server-id 37001 end_log_pos 353 CRC32 0x8b12bd3c Forespørgsel thread_id=19 exec_time=0 error_code=0SET TIMESTAMP=1582902585/@_!session/.ps1*do_/session/.ps1; SET! @@session.foreign_key_checks=1, @@session.sql_auto_is_null=0, @@session.unique_checks=1, @@session.autocommit=1, @@session.check_constraint_checks=1/*!*/;SET @@session. sql_mode=524288/*!*/;SET @@session.auto_increment_increment=1, @@session.auto_increment_offset=1/*!*/;SET @@session.character_set_client=255,@@session.collation_connection=255,@@ session.collation_server=255/*!*/;SET @@session.lc_time_names=0/*!*/;SET @@session.collation_database=DEFAULT/*!*/;BEGIN/*!*/;# at 353# 200228 15:09:45 server-id 370 01 end_log_pos 420 CRC32 0xe0e44a1b Table_map:`sbtest`.`sbtest1` afbildet til nummer 92# ved 420# ved 8625# ved 16830#200228 15:09:45 server-id 370001 ende_log_2c2c:8625# ved 16830#200228 15:09:45 server-id 370001 tabel 67001 37001 37001:67001, 25:09:45 09:45 server-id 37001 end_log_pos 16830 CRC32 0x89496a07 Delete_rows:tabel-id 92#200228 15:09:45 server-id 37001 end_log_pos 18765 CRC022 4_ST_22 1x322 1x322 1x32 18765 CRC22 1x322 1x32 18765 CRC22 1x32 1x32 1x32 18765 18765 CRC22 13b2 1x322 1x32 1x32 1_3b2 tabel
For nemt at slå op efter binlog-positionsnummeret, skal du være opmærksom på linjerne, der starter med "# ved ". Fra ovenstående linjer kan vi se, at DELETE-begivenheden fandt sted ved position 281 inde i binlog.000002 (starter ved "# ved 281"). Vi kan også hente binlog-begivenhederne direkte inde i en MySQL-server:
mysql> VIS BINLOGBEGIVENHEDER I 'binlog.000002';+---------+-------+------- ----------------------------------------- -------------------------------------------------- ---+| Log_navn | Pos | Hændelsestype | Server_id | End_log_pos | Info |+---------------+-------+----------------+------ -----+-------------+------------------------------------- --------------------------------------+| binlog.000002 | 4 | Format_desc | 37003 | 124 | Server ver:8.0.19, Binlog ver:4 || binlog.000002 | 124 | Previous_gtids | 37003 | 195 | 0d98d975-59f8-11ea-bd30-525400261060:1 || binlog.000002 | 195 | Gtid | 37001 | 281 | SET @@SESSION.GTID_NEXT='0d98d975-59f8-11ea-bd30-525400261060:2' || binlog.000002 | 281 | Forespørgsel | 37001 | 353 | BEGYND || binlog.000002 | 353 | Tabel_kort | 37001 | 420 | table_id:92 (sbtest.sbtest1) || binlog.000002 | 420 | Slet_rækker | 37001 | 8625 | table_id:92 || binlog.000002 | 8625 | Slet_rækker | 37001 | 16830 | table_id:92 || binlog.000002 | 16830 | Slet_rækker | 37001 | 18765 | table_id:92 flag:STMT_END_F || binlog.000002 | 18765 | Xid | 37001 | 18796 | COMMIT /* xid=171006 */ |+--------------+-------+---------------- +-----------+--------------+----------- --------------------------------------------+9 rækker i sæt ( 0,00 sek.)
Vi kan nu bekræfte, at position 281 er der, hvor vi ønsker, at vores data skal vende tilbage til. Vi kan derefter bruge --start-position flaget til at generere nøjagtige flashback hændelser. Bemærk, at vi udelader "-vv"-flaget og tilføj --flashback-flaget:
(slave2-mysql8)$ /root/mysqlbinlog \--start-position=281 \--database=sbtest \--table=sbtest1 \/var/lib/mysql/binlog.000002 \-- flashback> /root/flashback.binlog
Flashback.binlog indeholder alle de nødvendige hændelser for at fortryde alle ændringer, der er sket på tabel sbtest1 på denne MySQL-server. Da dette er en slaveknude i en replikeringsklynge, er vi nødt til at bryde replikationen på den valgte slave (slave2) for at bruge den til flashback-formål. For at gøre dette skal vi stoppe replikationen på den valgte slave, indstille MySQL GTID til ON_PERMISSIVE og gøre slaven skrivbar:
mysql> STOP SLAVE; SET GLOBAL gtid_mode =TIL_TILLADELSE; SET GLOBAL enforce_gtid_consistency =FRA; SET GLOBAL read_only =FRA;
På dette tidspunkt er slave2 ikke en del af replikationen, og vores topologi ser således ud:
Importer flashbacket via mysql-klienten, og vi ønsker ikke, at denne ændring skal være optaget i MySQL binær log:
(slave2-mysql8)$ mysql -uroot -p --init-command='SET sql_log_bin=0' sbtest
Vi kan derefter se alle de slettede rækker, som bevist af følgende udsagn:
mysql> VÆLG COUNT(id) FRA sbtest1 WHERE id MELLEM 5 og 100;+------+| COUNT(id) |+-----------+| 96 |+-----------+1 række i sæt (0,00 sek.)
Vi kan derefter oprette en SQL-dumpfil til tabel sbtest1 til vores reference:
(slave2-mysql8)$ mysqldump -uroot -p --single-transaction sbtest sbtest1> sbtest1_flashbacked.sql
Når flashback-operationen er fuldført, kan vi genføje slaveknuden tilbage i replikationskæden. Men for det første skal vi bringe databasen tilbage i en konsistent tilstand ved at afspille alle begivenheder fra den position, vi havde flashbacket. Glem ikke at springe binær logning over, da vi ikke ønsker at "skrive" på slaven og risikere os selv med fejlagtige transaktioner:
(slave2-mysql8)$ /root/mysqlbinlog \--start-position=281 \--database=sbtest \--table=sbtest1 \/var/lib/mysql/binlog.000002 | mysql -uroot -p --init-command='SET sql_log_bin=0' sbtest
Forbered endelig noden tilbage til sin rolle som MySQL-slave og start replikeringen:
mysql> SET GLOBAL read_only =ON;SET GLOBAL enforce_gtid_consistency =ON; SET GLOBAL gtid_mode =TIL; START SLAVE;
Bekræft, at slavenoden replikerer korrekt:
mysql> VIS SLAVESTATUS\G... Slave_IO_Running:Ja Slave_SQL_Running:Ja...
På dette tidspunkt har vi genforenet slaven tilbage i replikationskæden, og vores topologi er nu tilbage til sin oprindelige tilstand:
Råb op til MariaDB-teamet for at introducere denne forbløffende funktion!