トランザクション処理を 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 アルゴリズムで使用頻度の低いページは削除 |
ダーティページ | 更新したが、まだデータファイルに書き込まれていないページ |
※2 PostgreSQL ではWAL バッファ、Oracle ではログバッファと呼ぶ
※3 PostgreSQL WAL ログ、Oracle ではトランザクションログと呼ぶ。MySQL では ib_logfileN が実態。
PostgreSQL の対応するアーキテクチャ
上記を踏まえて、システム障害から復旧する技術を解説します。
クラッシュリカバリ
クラッシュリカバリは、システム障害から復旧する方法で、主に次の要素で構成されます。
- Redo フェーズ (ロールフォワード)
- Undo フェーズ (ロールフォワード)
- ロールバックセグメント
- Change buffer マージ ※本記事では未解説
- Purge 処理 ※本記事では未解説
また、クラッシュリカバリは、ダブルライトバッファで書き込み途中のクラッシュも検知します。
事前にロギングを行う仕組みは全て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 ログ | データファイル |
---|---|---|
アクセス | シーケンシャルアクセス (トランザクション操作を追記するだけ) | ランダムアクセスが多い (実データを更新するため) |
書き込み箇所 | 少ない (ログファイルのみ) | 多い (変更した全てのデータファイル) |
速度 | 早い | 遅い |
これにより、メモリからストレージにページが書き出され、永続化します。
この時、Redo ログに LSN (ログシーケンス番号) を書き込みます。
上記の場合、LSN=1 までの変更がデータファイルに書き込み完了していることがわかります。
システム障害が発生した場合、LSN を見ればどこからロールフォワードするか判断できます。
(上記の場合、LSN:2 からデータファイルへロールフォワードすればいい。)
動作確認
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//
--- 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
なお、ダーティページの数は以下のコマンドで確認できます。
+--------------------------------+-------+ | Variable_name | Value | +--------------------------------+-------+ | Innodb_buffer_pool_pages_dirty | 452 | +--------------------------------+-------+
※ダブルライトバッファはストレージにある小さなファイル (2MB 程度) です。
ダブルライトバッファに書き込む理由はデータファイルのデータを保護するためです。
データファイルの書き込み途中でシステム障害が発生すると、Redo ログでは復旧できません。
(壊れた実データに、Redo ログのトランザクション操作をしてもデータが復元できない)
そこで、ダブルライトバッファを使って、以下のようにデータファイルを保護します。
システム障害のタイミング | 対処 |
---|---|
ダブルライトバッファの書き込み中 | ダブルライトバッファを捨てるだけ (データファイルには、まだ書き込んでないので問題なし) |
データファイルへの書き込み中 | ダブルライトバッファからデータファイルを復元 |
ダーティページは、チェックポイントで定期的にデータファイルにフラッシュされます。(参考)
この時、COMMIT を行う前にシステム障害が発生すると、データファイルをロールバックする (更新前のデータに戻す) 必要があります。
ロールバックセグメントは、ロールバックを実現するために更新前のデータを持ちます。
(なお、更新前のデータは MVCC でも利用します)
最後に
関連記事
ACID 特性の記事一覧 | ||||
---|---|---|---|---|
データベースの基礎知識 | |||||
---|---|---|---|---|---|