【ACID 特性】Durability (永続性) をわかりやすく解説

DurabilityDurability とは、トランザクションを完了 (COMMIT) した結果が失われない特性です。

トランザクション処理COMMIT した結果が失われる状況は、主に次の2種類が考えられます。

障害の種類説明対策
システム障害OS や電源の障害等で、メモリ上のデータが消失ロールフォワード
- WAL (Redo ログ)
- チェックポイント
ロールバック
- ロールバックセグメント
ダブルライトバッファ
メディア障害ストレージの破損等で、メディア上のデータが消失バックアップ
Point-in-Time リカバリ
レプリケーション

なお、Durability は一般的に「システム障害」を対象とすることが多いようです。

Durability can be achieved by flushing the transaction's log records to non-volatile storage before acknowledging commitment.

https://en.wikipedia.org/wiki/Durability_(database_systems)

In addition, this recovery must be permanent, meaning all transactions must be reconstructed, even if the database server crashes due to OS failure or power loss.

https://www.techopedia.com/definition/27416/durability-databases

そのため、本記事では「システム障害」の対策方法を解説していきます。
(メディア障害への対策は、以下のバックアップレプリケーション記事をご覧ください)

データベースの基礎知識
ACID 特性の記事一覧
スポンサーリンク

システム障害への対策

システム障害への対策で基本的な考え方は、ストレージにデータを保存することです。
(電源が落ちるとメモリ上のデータは消えますが、ストレージ上のデータは残るため)

しかし、パフォーマンスの観点から、高速に読み書き可能なメモリにデータを置きたいです。

そのため、メモリ上でデータを処理し、メモリの内容をストレージに書き出します。

メモリとストレージが持つデータ

RDBMS におけるメモリストレージで保持するデータは、以下のとおりです。

用語説明
バッファプール ※1テーブルとインデックスなどを保持するメモリ上のキャッシュ
データファイルテーブルとインデックスなどを保持するストレージ上のファイル
ログバッファ ※2トランザクションの API コールを記録したメモリ上のバッファ
Redo ログ ※3トランザクションの API コールを記録したストレージ上のファイル
フラッシュメモリからストレージに書き込むこと
ページメモリを一定サイズに分割した単位
メモリストレージ間で、一度に転送できるデータ量
・複数の行データを格納可能
LRU アルゴリズムで使用頻度の低いページは削除
ダーティページ更新したが、まだデータファイルに書き込まれていないページ
※1 PostgreSQL では共有バッファ、Oracle ではデータベースバッファキャッシュと呼ぶ。MySQL の実態は ibd ファイル
※2 PostgreSQL ではWAL バッファ、Oracle ではログバッファと呼ぶ
※3 PostgreSQL WAL ログ、Oracle ではトランザクションログと呼ぶ。MySQL では ib_logfileN が実態。

MySQL (innodb) の対応するアーキテクチャ

https://dev.mysql.com/doc/refman/8.0/ja/innodb-architecture.html

※テーブルスペース = InnoDB テーブルやインデックスを保持するデータファイル

上記を踏まえて、システム障害から復旧する技術を解説します。

スポンサーリンク

クラッシュリカバリ

クラッシュリカバリは、システム障害から復旧する方法で、主に次の要素で構成されます。

また、クラッシュリカバリは、ダブルライトバッファで書き込み途中のクラッシュも検知します。

WAL (Write Ahead Log)

WAL とは、テーブルの変更などを、データファイルより先にログに書き込むことです。

事前にロギングを行う仕組みは全てWALと呼ばれる。MySQLで言えばInnoDBにおいてRedoログとして利用されるログもWALであるし、MySQL Clusterのディスクテーブル利用時のUndoログだってWALの一種である。

https://nippondanji.blogspot.com/2009/03/wal.html
バッファプールは行データなどテーブルの実際のデータを持ち、ログバッファはトランザクション操作を持ちます

なお、Redo ログへの書き込みが完了した時点で、COMMIT が成功した通知をクライアントに返します。(データファイルには、まだ書き込みをしてません。バイナリログも COMMIT で更新)

WAL によるシステム障害への対策

WAL でデータファイルより先にログへ書き込むことにより、システム障害を次のように防ぎます。

システム障害のタイミング対応
Redo ログに書き込み中Redo の内容を捨てる
データファイルにまだ書き込んでないので問題なし
データファイル書き込み中Redo ログから COMMIT した内容をデータファイルに復元
(ロールフォワードと言います)

WAL によるパフォーマンスの向上

WAL は、システム障害への対策だけではなく、COMMIT 時のパフォーマンスが良くなります。
(最後、データファイルにフラッシュする際のコストは別)

種類Redo ログデータファイル
アクセスシーケンシャルアクセス
(トランザクション操作を追記するだけ)
ランダムアクセスが多い
(実データを更新するため)
書き込み箇所少ない (ログファイルのみ)多い (変更した全てのデータファイル)
速度早い遅い

チェックポイント処理

チェックポイント処理とは、バッファプールからデータファイルにダーティページを書き込む (フラッシュ) することです。

これにより、メモリからストレージページが書き出され、永続化します。

チェックポイントを実施するタイミングはこちら
COMMIT は Redo ログに書き込まれるだけで、チェックポイント処理 (データファイルへのフラッシュ) は別で行います

この時、Redo ログに LSN (ログシーケンス番号) を書き込みます。

LSN (ログシーケンス番号)

LSN とは、バッファプールをどこまでデータファイルに書き込んだか示す番号です。

上記の場合、LSN=1 までの変更がデータファイルに書き込み完了していることがわかります。

システム障害が発生した場合、LSN を見ればどこからロールフォワードするか判断できます。
(上記の場合、LSN:2 からデータファイルへロールフォワードすればいい。)

動作確認

delimiter //
CREATE PROCEDURE insert_loop(max int)
BEGIN
 DECLARE id INT DEFAULT 0;
 REPEAT
  SET id = id + 1;
  INSERT INTO durability VALUES(id);
  IF(MOD(id, 10000) = 0) THEN COMMIT;
  END IF;
 UNTIL id >= max
 END REPEAT;
END//
delimiter ;
SET AUTOCOMMIT = 0;
CREATE TABLE durability(id int);
CALL insert_loop(500000);
SHOW ENGINE INNODB STATUS\G
---
LOG
---
Log sequence number          153604788
Log buffer assigned up to    153604788
Log buffer completed up to   153604788
Log written up to            153604028
Log flushed up to            153604028
Added dirty pages up to      153604788
Pages flushed up to          144025476
Last checkpoint at           144025476

以下の内容が確認できます。

  • LSN (Log sequence number) の値
  • ログバッファは最新情報
  • Redo ログの書き込みは、ログバッファより少し遅れている (COMMIT まで書き込まない)
  • バッファプールにあるダーティページは最新の LSN
  • Redo ログに先、データファイルに後に書き込んでいる (WAL)
  • チェックポイントレコードは、データファイルにフラッシュした最新の LSN

なお、ダーティページの数は以下のコマンドで確認できます。

SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_pages_dirty';
+--------------------------------+-------+
| Variable_name                  | Value |
+--------------------------------+-------+
| Innodb_buffer_pool_pages_dirty | 452   |
+--------------------------------+-------+

ダブルライトバッファ

ダブルライトバッファとは、データファイルに書き込む前に、小さなファイルに書き込みます

※ダブルライトバッファはストレージにある小さなファイル (2MB 程度) です。

ダブルライトバッファに書き込む理由はデータファイルのデータを保護するためです。

データファイルの書き込み途中でシステム障害が発生すると、Redo ログでは復旧できません。
(壊れた実データに、Redo ログのトランザクション操作をしてもデータが復元できない)

そこで、ダブルライトバッファを使って、以下のようにデータファイルを保護します。

システム障害のタイミング対処
ダブルライトバッファの書き込み中ダブルライトバッファを捨てるだけ
(データファイルには、まだ書き込んでないので問題なし)
データファイルへの書き込み中ダブルライトバッファからデータファイルを復元

ロールバックセグメント

ロールバックセグメントとは、データを元に戻すための Undo ログを記録する領域です。
undo ログの中身はここ

ダーティページは、チェックポイントで定期的にデータファイルにフラッシュされます。(参考)

この時、COMMIT を行う前にシステム障害が発生すると、データファイルロールバックする (更新前のデータに戻す) 必要があります。

ロールバックセグメントは、ロールバックを実現するために更新前のデータを持ちます。
(なお、更新前のデータは MVCC でも利用します)

スポンサーリンク

最後に

関連記事

ACID 特性の記事一覧
データベースの基礎知識

参考記事

トランザクションの設計と進化 | ドクセル
ドクセルはスライドやPDFをかんたんに共有できるサイトです