はじめに
この記事では、Oracle DatabaseのREDO Logの内部実装を深掘りします。
InnoDB、PostgreSQLに続く3本目です。OracleのREDO Logは歴史が長く、WALの原型ともいえる存在です。Oracle 19c / 23aiを中心に、以下を解説します:
- REDO Logの役割(InnoDBやPostgreSQLとの共通点)
- Change VectorとRedo Record(記録の単位)
- Redo Log Bufferと書き込みの仕組み(LGWR、Redo Strand)
- Online Redo LogとLog Switch(循環利用の仕組み)
- Checkpoint(InnoDBやPostgreSQLとの違い)
そもそもOracle REDO Logって何?
InnoDBのREDO Log、PostgreSQLのWALと同じ役割です。WAL(Write-Ahead Logging)の原則に従い、データブロックを変更する前にREDO情報をディスクに書きます。
Oracleの用語で整理すると:
Oracle用語 InnoDB用語 PostgreSQL用語
──────────────────────────────────────────────────────────
Redo Log REDO Log WAL
SCN (System Change LSN LSN
Number)
Redo Log Buffer Log Buffer WAL Buffer
LGWR log_writer バックエンド自身
Online Redo Log File ib_logfile0/1 WALセグメントファイル
Archived Redo Log ― WALアーカイブ
大きな違いはSCN(System Change Number)です。InnoDBやPostgreSQLのLSNがログの物理的なバイト位置を表すのに対し、OracleのSCNは論理的なタイムスタンプです。SCNはデータベース全体で単調増加する番号で、トランザクションの順序を決定します。
Change VectorとRedo Record
InnoDBでは65種類のREDOタイプ、PostgreSQLではResource Manager方式でした。OracleではChange VectorとRedo Recordという2層構造です。
Change Vector
Change Vectorは「1つのデータブロックに対する1つの変更」を記録する最小単位です。InnoDBの1 REDOレコードに相当します。
Change Vector:
| OpCode | Block Address | Change Data |
|---|---|---|
| (操作種別) | (DBA) | (変更内容) |
- **OpCode**:操作の種類(例:11.2 = INSERT、11.3 = DELETE、11.5 = UPDATE)
- **DBA(Data Block Address)**:対象ブロックのファイル番号+ブロック番号
- **Change Data**:変更内容
### Redo Record
Redo Recordは1つ以上のChange Vectorをまとめたものです。1つのアトミック操作(例:INSERT文)が複数ブロックを変更する場合、それらのChange Vectorが1つのRedo Recordにまとめられます。
Redo Record: – Redo Record Header – ├─ SCN – ├─ Thread Number – └─ Record Length – Change Vector 1 (UNDO block変更) – Change Vector 2 (Data block変更) – Change Vector 3 (Index block変更)
PostgreSQLの1 WALレコードが複数ページを参照できるのと似ていますね。InnoDBだけが「1レコード = 1ページ」の制約を持っています。
ALTER SYSTEM DUMP LOGFILEで実際に見てみる
ALTER SYSTEM DUMP LOGFILE '/path/to/redo01.log';トレースファイルに以下のような出力が得られます:
REDO RECORD - Thread:1 RBA: 0x000012.00000002.0010 LEN: 0x0200 VLD: 0x0d
SCN: 0x0000.001a3f50 SUBSCN: 1
CHANGE #1 TYP:0 CLS:1 AFN:4 DBA:0x01000080 OBJ:73142 SCN:0x0000.001a3f4e
Undo Block or Undo Segment Header
CHANGE #2 TYP:2 CLS:1 AFN:4 DBA:0x01400021 OBJ:73142 SCN:0x0000.001a3f4e
KDO Op code: URP -- Update Row Piece
Redo Log Bufferと書き込み
Redo Log Buffer
Redo Log BufferはSGA(System Global Area)内の共有メモリ領域です。サイズはLOG_BUFFERパラメータで設定します。
SHOW PARAMETER log_buffer;
-- デフォルト: 自動計算(通常数MB〜数十MB)書き込みの流れ
サーバープロセスがデータを変更すると:
1. サーバープロセス
├─ Change Vectorを生成
├─ Redo Log Bufferにコピー(redo copy latch / redo allocation latch)
└─ 完了
2. LGWR(Log Writer)プロセス
├─ Redo Log BufferからOnline Redo Logファイルに書き込み
└─ 以下のタイミングで起動:
├─ コミット時(ユーザーがCOMMIT発行)
├─ Redo Log Bufferが1/3埋まったとき
├─ 3秒ごと
└─ DBWnがダーティブロックを書く前
InnoDBのlog_writer/log_flusherと似た専用プロセス方式です。PostgreSQLの「各バックエンドが自分で書く」方式とは対照的です。
Redo Strand(Private Redo)
高負荷環境では、Redo Log Bufferへの書き込みがボトルネックになります。Oracle 12c以降、Private Redo(Redo Strand)という仕組みが導入されました。
従来:
全サーバープロセス → [共有Redo Log Buffer] → LGWR → ディスク
↑ ボトルネック
Private Redo(12c〜):
プロセスA → [Private Strand A]─┐
プロセスB → [Private Strand B]─┼→ LGWR → ディスク
プロセスC → [Private Strand C]─┘
各サーバープロセスが自分専用のStrandにREDOを書き、LGWRがまとめてディスクに書きます。InnoDBのMySQL 8.0 Lock-free設計(fetch_addで専用領域を確保)と発想が似ています。
Private Redoが使われるかどうかは自動的に判断されます。小さなトランザクションはPrivate Strandを使い、大きなトランザクション(LOBの更新など)は従来の共有Redo Log Bufferを使います。
Group Commit
OracleでもGroup Commitが行われます。複数のトランザクションが同時にコミットすると、LGWRは1回のI/Oでまとめて書き込みます。
-- コミット時の書き込み待ちの挙動を制御
ALTER SYSTEM SET commit_logging = 'BATCH'; -- バッチ書き込み
ALTER SYSTEM SET commit_wait = 'NOWAIT'; -- fsyncを待たない(危険)| 設定 | 挙動 | InnoDB対応 | PostgreSQL対応 |
|---|---|---|---|
| デフォルト | fsyncまで待つ | = 1 |
synchronous_commit = on |
commit_wait = NOWAIT |
待たない | = 0 |
synchronous_commit = off |
commit_logging = BATCH |
バッチ化 | ― | ― |
Partial Write問題:Oracleはどう対処するか?
InnoDBはDouble Write Buffer、PostgreSQLはFull Page Writeで対処していました。Oracleはどうでしょうか?
REDO Log側:問題になりにくい
OracleのREDO Logは512バイトのブロック単位で書き込まれます。ほとんどのストレージはセクタサイズ512Bの原子書き込みを保証するため、REDO Logブロックが中途半端に書かれる可能性は極めて低いです。
データブロック側:別の仕組みで保護
Oracleのデータブロック(デフォルト8KB)はPartial Writeの影響を受け得ます。Oracleは以下の仕組みで対処します:
- DB_BLOCK_CHECKSUM:各データブロックにチェックサムを記録。読み取り時に破損を検出
- Lost Write Protection(19c〜):Data Guardのスタンバイ側でブロックのSCNを比較し、プライマリ側のロストライトを検出
- DB_LOST_WRITE_PROTECT:Shadow Lost Write Protectionを有効化し、スタンバイなしでもロストライトを検出
InnoDBやPostgreSQLが「壊れたページをログから復元する」アプローチなのに対し、Oracleは「壊れたことを検出する」アプローチが中心です。OracleはASMやストレージレイヤーとの連携でI/O整合性を担保する設計思想があり、DB単体でページ復元まで行うInnoDBやPostgreSQLとはアーキテクチャの前提が異なります。
Online Redo LogとLog Switch
Online Redo Logの構造
OracleはOnline Redo Logをグループ単位で管理します。各グループには1つ以上のメンバー(物理ファイル)があります。同じグループのメンバーは同一内容のミラーコピーです。
Group 1: /u01/oradata/redo01a.log, /u02/oradata/redo01b.log ← ミラー
Group 2: /u01/oradata/redo02a.log, /u02/oradata/redo02b.log ← ミラー
Group 3: /u01/oradata/redo03a.log, /u02/oradata/redo03b.log ← ミラー
InnoDBのib_logfile0/1やPostgreSQLのWALセグメントにはミラーの概念がありません。Oracleはストレージ障害に対する耐性をREDO Log自体に組み込んでいます(ただし実際にはASMやRAIDで冗長化することが多い)。
Log Switch
グループは循環的に使われます。1つのグループが一杯になると、次のグループに切り替わります。これがLog Switchです。
Group 1 (CURRENT) → Group 2 (CURRENT) → Group 3 (CURRENT) → Group 1 ...
↓ ↓ ↓
ACTIVE/ ACTIVE/ ACTIVE/
INACTIVE INACTIVE INACTIVE
各グループのステータス: – CURRENT:LGWRが現在書き込み中 – ACTIVE:リカバリにまだ必要(Checkpointが完了していない) – INACTIVE:リカバリに不要(上書き可能)
SELECT group#, status, bytes/1024/1024 AS size_mb FROM v$log;
GROUP# STATUS SIZE_MB
------ -------- -------
1 CURRENT 200
2 INACTIVE 200
3 INACTIVE 200InnoDBはファイル内を循環利用しますが、Oracleはファイル(グループ)単位で切り替えます。PostgreSQLはセグメントファイルを作成/削除します。
Archived Redo Log
ARCHIVELOGモードでは、Log Switch時にLGWRが書き終わったグループをARCn(Archiver)プロセスがアーカイブ先にコピーします。これにより、Online Redo Logが上書きされても過去のREDO情報が保持され、PITR(Point-in-Time Recovery)が可能になります。
Online Redo Log Archived Redo Log
Group 1 ──Log Switch──→ /u01/archive/arc_0001.log
Group 2 ──Log Switch──→ /u01/archive/arc_0002.log
Group 3 ──Log Switch──→ /u01/archive/arc_0003.log
PostgreSQLのarchive_command/archive_libraryによるWALアーカイブと同じ概念です。InnoDBにはネイティブのアーカイブ機能がなく、バイナリログ(binlog)が別途その役割を担います。
Checkpoint
OracleのCheckpointの種類
Oracleには複数種類のCheckpointがあります:
| 種類 | トリガー | 動作 |
|---|---|---|
| Full Checkpoint | ALTER SYSTEM CHECKPOINT、DB停止時 |
全ダーティブロックをフラッシュ |
| Incremental Checkpoint | 定期的(自動) | DBWnが少しずつダーティブロックをフラッシュ |
| Log Switch Checkpoint | Log Switch時 | そのグループに関連するダーティブロックをフラッシュ |
Incremental Checkpoint
通常運用で最も重要なのはIncremental Checkpointです。OracleのDBWn(Database Writer)プロセスが、ダーティブロックを少しずつ継続的にフラッシュします。
これはInnoDBのPage Cleanerに近い動作です。PostgreSQLのCheckpointが「全ダーティページを一気にフラッシュする重い操作」だったのとは対照的です。
InnoDB: Page Cleanerが常時フラッシュ + Checkpointは軽い(LSN記録のみ)
Oracle: DBWnが常時フラッシュ + Incremental Checkpointで位置を進める
PostgreSQL: Checkpoint時に全ダーティページをフラッシュ(重い)
Checkpoint位置の管理
Oracleは制御ファイル(Control File)にCheckpoint情報を記録します。各データファイルのヘッダにも、そのファイルに関するCheckpoint SCNが記録されます。
SELECT file#, checkpoint_change# FROM v$datafile_header;
FILE# CHECKPOINT_CHANGE#
----- ------------------
1 1732456
2 1732456
3 1732450 ← このファイルだけ少し遅れているInnoDBがib_logfile0のCheckpoint Blockに記録し、PostgreSQLがpg_controlに記録するのと同様に、Oracleは制御ファイルに記録します。
まとめ
| トピック | InnoDB | PostgreSQL | Oracle |
|---|---|---|---|
| 名称 | REDO Log | WAL | REDO Log |
| 順序管理 | LSN(物理位置) | LSN(物理位置) | SCN(論理タイムスタンプ) |
| 記録単位 | REDOレコード(1ページ) | WALレコード(複数ページ可) | Redo Record(複数Change Vector) |
| Partial Write対策 | Double Write Buffer | Full Page Write | ― (ブロックサイズが小さい+ロストライト保護) |
| 書き込み主体 | 専用スレッド(log_writer) | 各バックエンド | 専用プロセス(LGWR) |
| 並列化 | Lock-free(fetch_add) | LWLock(複数スロット) | Private Redo Strand |
| ファイル管理 | 固定数ファイル循環 | セグメント作成/削除 | グループ単位で循環 + アーカイブ |
| ミラーリング | なし | なし | グループ内メンバーでミラー |
| Checkpoint | 軽い(LSN記録) | 重い(全ページフラッシュ) | Incremental(DBWnが常時フラッシュ) |
Oracleは最も歴史が長いだけあって、Online Redo Logのグループ/メンバー構造やArchived Redo Logなど、運用面での成熟度が高い設計です。Private Redo Strandによる並列化も、InnoDBのLock-free設計とは異なるアプローチで同じ問題を解決しています。
次回はSQL Server Transaction Logを同じ切り口で解説します。