MariaDBは、フラッシュバックと呼ばれる非常に優れた機能を導入しました。フラッシュバックは、インスタンス、データベース、またはテーブルを古いスナップショットにロールバックできるようにする機能です。従来、ポイントインタイムリカバリ(PITR)を実行するには、バックアップからデータベースを復元し、バイナリログを再生して、特定の時間または位置でデータベースの状態をロールフォワードしていました。
フラッシュバックを使用すると、データベースを過去のある時点にロールバックできます。これは、少し前に発生した過去を確認したい場合に、はるかに高速です。現在の日付と時刻に関連するデータの非常に古いスナップショットを表示する場合は、フラッシュバックを使用すると非効率になることがあります。遅延したスレーブからの復元、またはバックアップとバイナリログの再生からの復元がより適切なオプションである可能性があります。
この機能はMariaDBクライアントパッケージでのみ使用できますが、MySQLサーバーで使用できないわけではありません。このブログ投稿では、MySQLサーバーでこのすばらしい機能を使用する方法を紹介しています。
MariaDBフラッシュバック要件
MySQL上でMariaDBフラッシュバック機能を使用したい場合は、基本的に次のことができます。
- 次の設定でバイナリログを有効にします。
- binlog_format =ROW(MySQL 5.7.7以降のデフォルト)。
- binlog_row_image =FULL(MySQL 5.6以降のデフォルト)。
- MariaDB10.2.4以降のインストールからmsqlbinlogユーティリティを使用します。
- フラッシュバックは現在、DMLステートメント(INSERT、DELETE、UPDATE)でのみサポートされています。 MariaDBの今後のバージョンでは、現在のテーブルを予約済みの非表示のデータベースにコピーまたは移動し、フラッシュバックを使用するときにコピーまたは移動することで、DDLステートメント(DROP、TRUNCATE、ALTERなど)に対するフラッシュバックのサポートが追加されます。 >
フラッシュバックは、フルイメージ形式のバイナリログの既存のサポートを利用することで実現されるため、すべてのストレージエンジンをサポートします。フラッシュバックイベントはメモリに保存されることに注意してください。したがって、サーバーにこの機能のための十分なメモリがあることを確認する必要があります。
MariaDBフラッシュバックはどのように機能しますか?
MariaDBのmysqlbinlogユーティリティには、この目的のために2つの追加オプションがあります。
- -B、-flashback-フラッシュバック機能は、コミットされたデータを特別な時点にロールバックできます。
- -T、-table =[name]-このテーブルのみのエントリを一覧表示します(ローカルログのみ)。
--flashbackフラグがある場合とない場合のmysqlbinlog出力を比較することで、それがどのように機能するかを簡単に理解できます。次のステートメントがMariaDBサーバーで実行されていると考えてください。
MariaDB> DELETE FROM sbtest.sbtest1 WHERE id = 1;
フラッシュバックフラグがない場合、実際のDELETEbinlogイベントが表示されます。
$ 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 */
...
上記のmysqlbinlogコマンドを--flashbackで拡張すると、DELETEイベントがINSERTイベントに変換され、それぞれのWHERE句とSET句と同様になります。
$ 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 */
...
行ベースのレプリケーション(binlog_format =ROW)では、各行変更イベントに2つの画像が含まれます。「前」の画像(INSERTを除く)で、更新する行を検索するときに列が照合されます。変更を含む「後」の画像(DELETEを除く)。 binlog_row_image =FULLを使用すると、MariaDBは前と後の両方の画像の完全な行(つまり、すべての列)をログに記録します。
次の例は、UPDATEのバイナリログイベントを示しています。次のステートメントがMariaDBサーバーで実行されていると考えてください。
MariaDB> UPDATE sbtest.sbtest1 SET k = 0 WHERE id = 5;
上記のステートメントのbinlogイベントを見ると、次のようになります。
$ 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
...
--flashbackフラグを使用すると、「前」の画像が既存の行の「後」の画像と交換されます。
$ 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 */
...
次に、フラッシュバック出力をMySQLクライアントにリダイレクトして、データベースまたはテーブルを目的の時点にロールバックできます。その他の例を次のセクションに示します。
MariaDBには、この機能専用のナレッジベースページがあります。 MariaDBフラッシュバックナレッジベースページを確認してください。
MySQLを使用したMariaDBフラッシュバック
MySQLのフラッシュバック機能を使用するには、次のことを行う必要があります。
- 任意のMariaDBサーバー(10.2.4以降)からmysqlbinlogユーティリティをコピーします。
次の単純なMySQL8.0レプリケーショントポロジがあるとします。
この例では、MySQL 8.0スレーブ(slave2)の1つに最新のMariaDB10.4からmysqlbinlogユーティリティをコピーしました。
(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
MariaDBのmysqlbinlogユーティリティは、slave2の/ root/mysqlbinlogにあります。 MySQLマスターで、次の悲惨なステートメントを実行しました:
mysql> DELETE FROM sbtest1 WHERE id BETWEEN 5 AND 100;
Query OK, 96 rows affected (0.01 sec)
上記のステートメントで96行が削除されました。スレーブサーバーで悲惨なイベントのbinlog位置を見つけようとする前に、イベントがマスターからすべてのスレーブに複製されるまで数秒待ちます。最初のステップは、そのサーバー上のすべてのバイナリログを取得することです。
mysql> SHOW BINARY LOGS;
+---------------+-----------+-----------+
| Log_name | File_size | Encrypted |
+---------------+-----------+-----------+
| binlog.000001 | 850 | No |
| binlog.000002 | 18796 | No |
+---------------+-----------+-----------+
悲惨なイベントは、このサーバーの最新のバイナリログであるbinlog.000002内に存在する必要があります。次に、MariaDBのmysqlbinlogユーティリティを使用して、10分前以降のテーブルsbtest1のすべてのbinlogイベントを取得できます。
(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
binlogの位置番号を簡単に検索するには、「#at」で始まる行に注意してください。上記の行から、binlog.000002内の位置281でDELETEイベントが発生していたことがわかります(「#at281」から始まります)。 MySQLサーバー内で直接binlogイベントを取得することもできます:
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)
これで、位置281がデータを元に戻す場所であることを確認できます。次に、-start-positionフラグを使用して、正確なフラッシュバックイベントを生成できます。 「-vv」フラグを省略していることに注意してください --flashbackフラグを追加します:
(slave2-mysql8)$ /root/mysqlbinlog \
--start-position=281 \
--database=sbtest \
--table=sbtest1 \
/var/lib/mysql/binlog.000002 \
--flashback > /root/flashback.binlog
flashback.binlogには、このMySQLサーバーのテーブルsbtest1で発生したすべての変更を元に戻すために必要なすべてのイベントが含まれています。これはレプリケーションクラスターのスレーブノードであるため、フラッシュバックの目的で使用するには、選択したスレーブ(slave2)でレプリケーションを中断する必要があります。これを行うには、選択したスレーブでレプリケーションを停止し、MySQL GTIDをON_PERMISSIVEに設定して、スレーブを書き込み可能にする必要があります。
mysql> STOP SLAVE;
SET GLOBAL gtid_mode = ON_PERMISSIVE;
SET GLOBAL enforce_gtid_consistency = OFF;
SET GLOBAL read_only = OFF;
この時点では、slave2はレプリケーションの一部ではなく、トポロジは次のようになっています。
mysqlクライアントを介してフラッシュバックをインポートしますが、この変更を望まない場合MySQLバイナリログに記録:
(slave2-mysql8)$ mysql -uroot -p --init-command='SET sql_log_bin=0' sbtest < /root/flashback.binlog
次のステートメントで証明されるように、削除されたすべての行を確認できます。
mysql> SELECT COUNT(id) FROM sbtest1 WHERE id BETWEEN 5 and 100;
+-----------+
| COUNT(id) |
+-----------+
| 96 |
+-----------+
1 row in set (0.00 sec)
次に、参照用にテーブルsbtest1のSQLダンプファイルを作成できます。
(slave2-mysql8)$ mysqldump -uroot -p --single-transaction sbtest sbtest1 > sbtest1_flashbacked.sql
フラッシュバック操作が完了すると、スレーブノードをレプリケーションチェーンに再参加させることができます。ただし、最初に、フラッシュバックした位置から開始するすべてのイベントを再生することにより、データベースを一貫した状態に戻す必要があります。スレーブに「書き込み」をしたくないので、バイナリロギングをスキップすることを忘れないでください。誤ったトランザクションで自分自身を危険にさらします:
(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
最後に、ノードをMySQLスレーブとしての役割に戻す準備をして、レプリケーションを開始します。
mysql> SET GLOBAL read_only = ON;
SET GLOBAL enforce_gtid_consistency = ON;
SET GLOBAL gtid_mode = ON;
START SLAVE;
mysql> SHOW SLAVE STATUS\G
...
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
...
この時点で、スレーブをレプリケーションチェーンに再参加させ、トポロジを元の状態に戻しました。
この驚くべき機能を紹介してくれたMariaDBチームに声をかけてください!
>