MariaDB heeft een erg coole functie geïntroduceerd genaamd Flashback. Flashback is een functie waarmee instanties, databases of tabellen kunnen worden teruggedraaid naar een oude momentopname. Om een point-in-time recovery (PITR) uit te voeren, zou men traditioneel een database herstellen vanaf een back-up en de binaire logs opnieuw afspelen om de databasestatus op een bepaalde tijd of positie vooruit te rollen.
Met Flashback kan de database worden teruggedraaid naar een punt in het verleden, wat veel sneller is als we gewoon het verleden willen zien dat nog niet zo lang geleden is gebeurd. Af en toe kan het gebruik van flashback inefficiënt zijn als u een zeer oude momentopname van uw gegevens wilt zien ten opzichte van de huidige datum en tijd. Herstellen vanaf een vertraagde slave, of vanaf een back-up plus het opnieuw afspelen van het binaire logboek zijn wellicht de betere opties.
Deze functie is alleen beschikbaar in het MariaDB-clientpakket, maar dat betekent niet dat we deze niet kunnen gebruiken met onze MySQL-servers. Deze blogpost laat zien hoe we deze geweldige functie op een MySQL-server kunnen gebruiken.
MariaDB Flashback-vereisten
Voor degenen die de MariaDB-flashbackfunctie naast MySQL willen gebruiken, kunnen we in principe het volgende doen:
- Schakel binair logboek in met de volgende instelling:
- binlog_format =ROW (standaard sinds MySQL 5.7.7).
- binlog_row_image =FULL (standaard sinds MySQL 5.6).
- Gebruik het hulpprogramma msqlbinlog vanaf elke MariaDB 10.2.4 en latere installatie.
- Flashback wordt momenteel alleen ondersteund via DML-instructies (INSERT, DELETE, UPDATE). Een aankomende versie van MariaDB zal ondersteuning toevoegen voor flashback via DDL-statements (DROP, TRUNCATE, ALTER, etc.) door de huidige tabel te kopiëren of te verplaatsen naar een gereserveerde en verborgen database, en vervolgens te kopiëren of terug te gaan wanneer flashback wordt gebruikt.
De flashback wordt bereikt door gebruik te maken van bestaande ondersteuning voor binaire logbestanden in volledig beeldformaat, waardoor het alle opslag-engines ondersteunt. Merk op dat de flashback-gebeurtenissen in het geheugen worden opgeslagen. Zorg er daarom voor dat uw server voldoende geheugen heeft voor deze functie.
Hoe werkt MariaDB Flashback?
MariaDB's mysqlbinlog-hulpprogramma wordt voor dit doel geleverd met twee extra opties:
- -B, --flashback - Flashback-functie kan uw vastgelegde gegevens terugdraaien naar een speciaal tijdstip.
- -T, --table=[name] - Lijst met vermeldingen voor alleen deze tabel (alleen lokaal logboek).
Door de uitvoer van mysqlbinlog te vergelijken met en zonder de vlag --flashback, kunnen we gemakkelijk begrijpen hoe het werkt. Overweeg dat de volgende instructie wordt uitgevoerd op een MariaDB-server:
MariaDB> DELETE FROM sbtest.sbtest1 WHERE id = 1;
Zonder flashback-vlag zien we de daadwerkelijke DELETE binlog-gebeurtenis:
$ mysqlbinlog -vv \
--start-datetime="$(date '+%F %T' -d 'now - 10 minutes')" \
--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_F
BINLOG '
6rxXXhOJkAAAQwAAAP06AxsAAO4AAAAAAAEABnNidGVzdAAHc2J0ZXN0MQAEAwP+/gTu4P7wAAEB
AAID/P8AFuAQfA==
6rxXXiCJkAAA4QAAAN47AxsAAO4AAAAAAAEAAgAE/wABAAAAVJ4HAHcAODM4Njg2NDE5MTItMjg3
NzM5NzI4MzctNjA3MzYxMjA0ODYtNzUxNjI2NTk5MDYtMjc1NjM1MjY0OTQtMjAzODE4ODc0MDQt
NDE1NzY0MjIyNDEtOTM0MjY3OTM5NjQtNTY0MDUwNjUxMDItMzM1MTg0MzIzMzA7Njc4NDc5Njcz
NzctNDgwMDA5NjMzMjItNjI2MDQ3ODUzMDEtOTE0MTU0OTE4OTgtOTY5MjY1MjAyOTHtSKLa
'/*!*/;
### DELETE FROM `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 nullable=0 is_null=0 */
### @4='67847967377-48000963322-62604785301-91415491898-96926520291' /* STRING(240) meta=65264 nullable=0 is_null=0 */
...
Door het bovenstaande mysqlbinlog-commando uit te breiden met --flashback, kunnen we zien dat de DELETE-gebeurtenis wordt geconverteerd naar een INSERT-gebeurtenis en vergelijkbaar met de respectievelijke WHERE- en SET-clausules:
$ mysqlbinlog -vv \
--start-datetime="$(date '+%F %T' -d 'now - 10 minutes')" \
--database=sbtest \
--table=sbtest1 \
/var/lib/mysql/binlog.000003 \
--flashback
...
BINLOG '
6rxXXhOJkAAAQwAAAP06AxsAAO4AAAAAAAEABnNidGVzdAAHc2J0ZXN0MQAEAwP+/gTu4P7wAAEB
AAID/P8AFuAQfA==
6rxXXh6JkAAA4QAAAN47AxsAAO4AAAAAAAEAAgAE/wABAAAAVJ4HAHcAODM4Njg2NDE5MTItMjg3
NzM5NzI4MzctNjA3MzYxMjA0ODYtNzUxNjI2NTk5MDYtMjc1NjM1MjY0OTQtMjAzODE4ODc0MDQt
NDE1NzY0MjIyNDEtOTM0MjY3OTM5NjQtNTY0MDUwNjUxMDItMzM1MTg0MzIzMzA7Njc4NDc5Njcz
NzctNDgwMDA5NjMzMjItNjI2MDQ3ODUzMDEtOTE0MTU0OTE4OTgtOTY5MjY1MjAyOTHtSKLa
'/*!*/;
### 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 nullable=0 is_null=0 */
...
Bij replicatie op basis van rijen (binlog_format=ROW) bevat elke gebeurtenis voor het wijzigen van een rij twee afbeeldingen, een 'voor'-afbeelding (behalve INSERT) waarvan de kolommen worden vergeleken bij het zoeken naar de rij die moet worden bijgewerkt, en een "na"-afbeelding (behalve DELETE) met de wijzigingen. Met binlog_row_image=FULL logt MariaDB volledige rijen (dat wil zeggen alle kolommen) voor zowel de voor- als na-afbeeldingen.
Het volgende voorbeeld toont binaire logboekgebeurtenissen voor UPDATE. Overweeg dat de volgende instructie wordt uitgevoerd op een MariaDB-server:
MariaDB> UPDATE sbtest.sbtest1 SET k = 0 WHERE id = 5;
Als we naar de binlog-gebeurtenis voor de bovenstaande verklaring kijken, zien we zoiets als dit:
$ mysqlbinlog -vv \
--start-datetime="$(date '+%F %T' -d 'now - 5 minutes')" \
--database=sbtest \
--table=sbtest1 \
/var/lib/mysql/binlog.000001
...
### UPDATE `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' /* STRING(240) meta=65264 nullable=0 is_null=0 */
### 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 */
# Number of rows: 1
...
Met de --flashback vlag wordt de "before" afbeelding verwisseld met de "after" afbeelding van de bestaande rij:
$ mysqlbinlog -vv \
--start-datetime="$(date '+%F %T' -d 'now - 5 minutes')" \
--database=sbtest \
--table=sbtest1 \
/var/lib/mysql/binlog.000001 \
--flashback
...
### UPDATE `sbtest`.`sbtest1`
### WHERE
### @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 */
### 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 is_null=0 */
...
We kunnen dan de flashback-uitvoer omleiden naar de MySQL-client, en zo de database of tabel terugdraaien naar het gewenste tijdstip. Meer voorbeelden worden getoond in de volgende secties.
MariaDB heeft een speciale kennisbankpagina voor deze functie. Bekijk de MariaDB Flashback-kennisbankpagina.
MariaDB Flashback met MySQL
Om de flashback-mogelijkheid voor MySQL te hebben, moet men het volgende doen:
- Kopieer het hulpprogramma mysqlbinlog van een MariaDB-server (10.2.4 of hoger).
- Schakel MySQL GTID uit voordat u het flashback-SQL-bestand toepast. Globale variabelen gtid_mode en force_gtid_consistency kunnen in runtime worden ingesteld sinds MySQL 5.7.5.
Stel dat we de volgende eenvoudige MySQL 8.0-replicatietopologie hebben:
In dit voorbeeld hebben we het hulpprogramma mysqlbinlog gekopieerd van de nieuwste MariaDB 10.4 op een van onze MySQL 8.0-slaves (slave2):
(mariadb-server)$ scp /bin/mysqlbinlog [email protected]:/root/
(slave2-mysql8)$ ls -l /root/mysqlbinlog
-rwxr-xr-x. 1 root root 4259504 Feb 27 13:44 /root/mysqlbinlog
Het mysqlbinlog-hulpprogramma van onze MariaDB bevindt zich nu in /root/mysqlbinlog op slave2. Op de MySQL-master hebben we de volgende rampzalige verklaring uitgevoerd:
mysql> DELETE FROM sbtest1 WHERE id BETWEEN 5 AND 100;
Query OK, 96 rows affected (0.01 sec)
96 rijen zijn verwijderd in de bovenstaande verklaring. Wacht een paar seconden om de gebeurtenissen van de master naar alle slaven te laten repliceren voordat we kunnen proberen de binlogpositie van de rampzalige gebeurtenis op de slave-server te vinden. De eerste stap is om alle binaire logs op die server op te halen:
mysql> SHOW BINARY LOGS;
+---------------+-----------+-----------+
| Log_name | File_size | Encrypted |
+---------------+-----------+-----------+
| binlog.000001 | 850 | No |
| binlog.000002 | 18796 | No |
+---------------+-----------+-----------+
Onze rampzalige gebeurtenis zou binnen binlog.000002 moeten staan, het laatste binaire logboek op deze server. We kunnen dan het mysqlbinlog-hulpprogramma van MariaDB gebruiken om alle binlog-gebeurtenissen voor tabel sbtest1 op te halen sinds 10 minuten geleden:
(slave2-mysql8)$ /root/mysqlbinlog -vv \
--start-datetime="$(date '+%F %T' -d 'now - 10 minutes')" \
--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
# Ignorable event type 33 (MySQL Gtid)
# at 281
#200228 15:09:45 server id 37001 end_log_pos 353 CRC32 0x8b12bd3c Query thread_id=19 exec_time=0 error_code=0
SET TIMESTAMP=1582902585/*!*/;
SET @@session.pseudo_thread_id=19/*!*/;
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 37001 end_log_pos 420 CRC32 0xe0e44a1b Table_map: `sbtest`.`sbtest1` mapped to number 92
# at 420
# at 8625
# at 16830
#200228 15:09:45 server id 37001 end_log_pos 8625 CRC32 0x99b1a8fc Delete_rows: table id 92
#200228 15:09:45 server id 37001 end_log_pos 16830 CRC32 0x89496a07 Delete_rows: table id 92
#200228 15:09:45 server id 37001 end_log_pos 18765 CRC32 0x302413b2 Delete_rows: table id 92 flags: STMT_END_F
Om gemakkelijk het binlog-positienummer op te zoeken, let op de regels die beginnen met "# bij ". Uit de bovenstaande regels kunnen we zien dat de DELETE-gebeurtenis plaatsvond op positie 281 in binlog.000002 (begint bij "# op 281"). We kunnen de binlog-gebeurtenissen ook rechtstreeks binnen een MySQL-server ophalen:
mysql> SHOW BINLOG EVENTS IN 'binlog.000002';
+---------------+-------+----------------+-----------+-------------+-------------------------------------------------------------------+
| Log_name | Pos | Event_type | 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 | Query | 37001 | 353 | BEGIN |
| binlog.000002 | 353 | Table_map | 37001 | 420 | table_id: 92 (sbtest.sbtest1) |
| binlog.000002 | 420 | Delete_rows | 37001 | 8625 | table_id: 92 |
| binlog.000002 | 8625 | Delete_rows | 37001 | 16830 | table_id: 92 |
| binlog.000002 | 16830 | Delete_rows | 37001 | 18765 | table_id: 92 flags: STMT_END_F |
| binlog.000002 | 18765 | Xid | 37001 | 18796 | COMMIT /* xid=171006 */ |
+---------------+-------+----------------+-----------+-------------+-------------------------------------------------------------------+
9 rows in set (0.00 sec)
We kunnen nu bevestigen dat positie 281 is waar we onze gegevens naartoe willen laten terugkeren. We kunnen dan de --start-position vlag gebruiken om nauwkeurige flashback-gebeurtenissen te genereren. Merk op dat we de vlag "-vv" weglaten en voeg de --flashback vlag toe:
(slave2-mysql8)$ /root/mysqlbinlog \
--start-position=281 \
--database=sbtest \
--table=sbtest1 \
/var/lib/mysql/binlog.000002 \
--flashback > /root/flashback.binlog
De flashback.binlog bevat alle vereiste gebeurtenissen om alle wijzigingen ongedaan te maken die zijn gebeurd in tabel sbtest1 op deze MySQL-server. Aangezien dit een slave-knooppunt van een replicatiecluster is, moeten we de replicatie op de gekozen slave (slave2) verbreken om deze voor flashback-doeleinden te gebruiken. Om dit te doen, moeten we de replicatie op de gekozen slave stoppen, MySQL GTID instellen op ON_PERMISSIVE en de slave beschrijfbaar maken:
mysql> STOP SLAVE;
SET GLOBAL gtid_mode = ON_PERMISSIVE;
SET GLOBAL enforce_gtid_consistency = OFF;
SET GLOBAL read_only = OFF;
Op dit moment maakt slave2 geen deel uit van de replicatie en onze topologie ziet er als volgt uit:
Importeer de flashback via mysql-client en we willen niet dat deze wijziging wordt doorgevoerd opgenomen in MySQL binair logboek:
(slave2-mysql8)$ mysql -uroot -p --init-command='SET sql_log_bin=0' sbtest < /root/flashback.binlog
We kunnen dan alle verwijderde rijen zien, zoals blijkt uit de volgende verklaring:
mysql> SELECT COUNT(id) FROM sbtest1 WHERE id BETWEEN 5 and 100;
+-----------+
| COUNT(id) |
+-----------+
| 96 |
+-----------+
1 row in set (0.00 sec)
We kunnen dan een SQL-dumpbestand maken voor tabel sbtest1 voor onze referentie:
(slave2-mysql8)$ mysqldump -uroot -p --single-transaction sbtest sbtest1 > sbtest1_flashbacked.sql
Zodra de flashback-bewerking is voltooid, kunnen we het slave-knooppunt weer in de replicatieketen voegen. Maar eerst moeten we de database terugbrengen naar een consistente staat, door alle gebeurtenissen opnieuw af te spelen vanaf de positie die we hadden geflashbackt. Vergeet niet om binaire logging over te slaan omdat we niet willen "schrijven" op de slave en onszelf riskeren met foutieve transacties:
(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
Bereid tot slot de node weer voor op zijn rol als MySQL-slave en start de replicatie:
mysql> SET GLOBAL read_only = ON;
SET GLOBAL enforce_gtid_consistency = ON;
SET GLOBAL gtid_mode = ON;
START SLAVE;
Controleer of het slaveknooppunt correct repliceert:
mysql> SHOW SLAVE STATUS\G
...
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
...
Op dit punt hebben we de slaaf weer toegevoegd aan de replicatieketen en onze topologie is nu terug in de oorspronkelijke staat:
Shout out naar het MariaDB-team voor de introductie van deze verbazingwekkende functie!