はじめに
この記事では、SQL ServerのTransaction Logの内部実装を深掘りします。
InnoDB、PostgreSQL、Oracleに続く4本目です。SQL ServerのTransaction Logは独自の設計が多く、特にVLF(Virtual Log File)という概念が特徴的です。SQL Server 2019 / 2022を中心に、以下を解説します:
- Transaction Logの役割
- Log Record(記録の単位)
- VLF(Virtual Log File)とログファイルの物理構造
- Log Bufferと書き込みの仕組み
- Checkpoint(Indirect Checkpointの登場)
- Recovery Model(復旧モデル)
そもそもTransaction Logって何?
InnoDBのREDO Log、PostgreSQLのWAL、OracleのREDO Logと同じ役割です。WALの原則に従い、データページを変更する前にログをディスクに書きます。
SQL Serverの用語で整理すると:
SQL Server用語 InnoDB用語 PostgreSQL用語 Oracle用語
──────────────────────────────────────────────────────────────────────────────
Transaction Log REDO Log WAL REDO Log
LSN LSN LSN SCN
Log Buffer Log Buffer WAL Buffer Redo Log Buffer
Log Writer log_writer バックエンド自身 LGWR
VLF ― WALセグメント Redo Logグループ
SQL ServerのLSNはInnoDBやPostgreSQLと同様に物理位置ベースですが、形式が独特です:
LSN = VLF Sequence Number : Log Block Offset : Slot Number
例: 00000025:00000048:0001
3つの部分からなり、VLF番号・ブロック内オフセット・スロット番号でログレコードの位置を一意に特定します。
Log Recordの構造
SQL Serverでは、各変更がLog Recordとして記録されます。
Log Recordの種類
主要なLog Record Typeはfn_dblog関数で確認できます:
SELECT [Current LSN], [Operation], [Context], [Transaction ID],
[Page ID], [AllocUnitName]
FROM fn_dblog(NULL, NULL);
Current LSN Operation Context Page ID
------------------------ --------------- ----------- --------
00000025:00000048:0001 LOP_BEGIN_XACT LCX_NULL NULL
00000025:00000048:0002 LOP_INSERT_ROWS LCX_HEAP 0001:0000009A
00000025:00000048:0003 LOP_INSERT_ROWS LCX_INDEX_LEAF 0001:000000A1
00000025:00000050:0001 LOP_COMMIT_XACT LCX_NULL NULL主要な操作タイプ: – LOP_BEGIN_XACT / LOP_COMMIT_XACT:トランザクション開始/コミット – LOP_INSERT_ROWS:行の挿入 – LOP_MODIFY_ROW:行の更新 – LOP_DELETE_ROWS:行の削除
InnoDBとの違い:REDO + UNDOが1つのログに
SQL Serverの最大の特徴は、REDO情報とUNDO情報が同じTransaction Logに記録されることです。
| InnoDB | PostgreSQL | Oracle | SQL Server | |
|---|---|---|---|---|
| REDO | REDO Log | WAL | REDO Log | Transaction Log |
| UNDO | 別ファイル(UNDO Tablespace) | テーブル内(MVCC) | 別領域(UNDO Segment) | 同じTransaction Log |
InnoDBやOracleではUNDO情報は別の場所に保存されますが、SQL ServerではTransaction Logに「この変更を元に戻すにはこうする」という情報も一緒に記録されます。これにより、ロールバック時もTransaction Logだけで完結します。
VLF(Virtual Log File)
SQL Serverのログファイル管理で最も特徴的なのがVLFです。
VLFとは
物理的なログファイル(.ldf)は内部的にVLF(Virtual Log File)という論理単位に分割されます。VLFはログの循環利用と管理の単位です。
物理ログファイル (.ldf)
| VLF 1 | VLF 2 | VLF 3 | VLF 4 | VLF 5 |
|---|---|---|---|---|
| INACTIVE | ACTIVE | ACTIVE | CURRENT | FREE |
各VLFのステータス:
- **ACTIVE**:アクティブなトランザクションやリカバリに必要
- **CURRENT**:現在書き込み中
- **FREE / INACTIVE**:再利用可能
InnoDBはファイル全体を循環利用し、PostgreSQLはセグメントファイルを作成/削除し、Oracleはグループ単位で切り替えます。SQL ServerはVLFという**ファイル内の論理区画**で管理するのが独特です。
### VLFの確認
```sql
DBCC LOGINFO;
FileId FileSize StartOffset FSeqNo Status Parity CreateLSN
------ ---------- ----------- ------ ------ ------ ---------
2 2031616 8192 37 2 64 0
2 2031616 2039808 38 2 64 0
2 2031616 4071424 39 2 128 0
2 2031616 6103040 40 0 0 0
- Status 2:アクティブ(再利用不可)
- Status 0:非アクティブ(再利用可能)
- FSeqNo:VLFのシーケンス番号
VLF数の問題
ログファイルの拡張方法によってVLF数が変わります。小刻みに自動拡張を繰り返すとVLFが大量に作られ、リカバリ性能が劣化します。
-- 悪い例:小さい自動拡張 → VLFが大量に
ALTER DATABASE MyDB MODIFY FILE (NAME = MyDB_log, FILEGROWTH = 1MB);
-- 良い例:適切なサイズで事前確保
ALTER DATABASE MyDB MODIFY FILE (NAME = MyDB_log, SIZE = 8GB, FILEGROWTH = 1GB);VLF数の目安:数十〜数百が適正。数千以上は問題です。
Partial Write問題:SQL Serverはどう対処するか?
InnoDBはDouble Write Buffer、PostgreSQLはFull Page Write、Oracleは512Bブロック書き込み+Checksumで対処していました。SQL Serverはどうでしょうか?
Transaction Log側:問題になりにくい
SQL ServerのTransaction Logも512バイトのLog Block単位で書き込まれます。Oracleと同様に、セクタサイズの原子書き込みが保証される範囲なので、ログ側のPartial Writeは問題になりにくいです。
データページ側:Torn Page Detection
SQL Serverはデータページ(8KB)のPartial Write対策としてTorn Page Detectionを提供します:
-- Torn Page Detectionを有効化
ALTER DATABASE MyDB SET PAGE_VERIFY TORN_PAGE_DETECTION;
-- より強力なChecksum検証(SQL Server 2005〜、デフォルト)
ALTER DATABASE MyDB SET PAGE_VERIFY CHECKSUM;- TORN_PAGE_DETECTION:各512Bセクタにビットを記録し、ページが完全に書かれたか検証
- CHECKSUM(デフォルト):ページ全体のチェックサムで破損を検出
Oracleと同様に「破損を検出する」アプローチです。InnoDBのDouble Write BufferやPostgreSQLのFull Page Writeのように「ログから復元する」アプローチとは異なります。
Log Bufferと書き込み
Log Buffer
SQL ServerのLog Bufferはメモリ上の循環バッファで、サイズは自動管理されます(通常数MB)。InnoDBやOracleのように明示的にサイズを設定するパラメータはありません。
書き込みの流れ
SQL Serverの書き込みモデルはOracleに近く、Log Writerという専用スレッドがディスクへの書き込みを担当します:
1. ワーカースレッド
├─ Log Recordを生成
├─ Log Bufferにコピー(spinlockで保護)
└─ コミット時にLog Writerに通知
2. Log Writer
├─ Log BufferからディスクにI/O発行
└─ 完了後、待機中のワーカースレッドを起床
Log Block
Log Bufferのデータは512バイトのLog Block単位でディスクに書かれます。InnoDBのBlockサイズと同じです。
Log Block (512 bytes):
- Log Block Header
- Log Records ...
- Padding (残り領域)
### 各DBの書き込みモデル比較
| | InnoDB | PostgreSQL | Oracle | SQL Server |
|---|---|---|---|---|
| 書き込み主体 | log_writer | 各バックエンド | LGWR | Log Writer |
| 並列挿入 | Lock-free(fetch_add) | LWLock(8スロット) | Private Redo Strand | Spinlock |
| Group Commit | あり | あり(fsync共有) | あり | あり |
## Checkpoint
### 従来のCheckpoint
SQL Server 2012以前は、`recovery interval`設定に基づいて定期的にCheckpointが発生し、ダーティページをディスクにフラッシュしていました。PostgreSQLのCheckpointに近い動作です。
### Indirect Checkpoint(SQL Server 2016〜)
SQL Server 2016以降、**Indirect Checkpoint**がデフォルトになりました。これはOracleのIncremental CheckpointやInnoDBのPage Cleanerに近い考え方です。
```sql
-- Target Recovery Time: リカバリにかかる目標時間
ALTER DATABASE MyDB SET TARGET_RECOVERY_TIME = 60 SECONDS; -- デフォルト
Indirect Checkpointでは、バックグラウンドで継続的にダーティページをフラッシュします。目標リカバリ時間(TARGET_RECOVERY_TIME)を達成できるペースでフラッシュを進めます。
従来のCheckpoint:
────────────────┤ Checkpoint! 全ダーティページフラッシュ ├────────
↑ I/Oスパイク
Indirect Checkpoint:
──╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌──
↑ 常時少しずつフラッシュ(I/Oが平準化)
各DBのCheckpoint比較
| InnoDB | PostgreSQL | Oracle | SQL Server | |
|---|---|---|---|---|
| 方式 | 軽い(LSN記録のみ) | 重い(全ページフラッシュ) | Incremental | Indirect(継続的フラッシュ) |
| ダーティページフラッシュ | Page Cleaner(常時) | Checkpointer + BGWriter | DBWn(常時) | Background(常時) |
| I/O特性 | 平準化 | スパイクあり | 平準化 | 平準化 |
Recovery Model(復旧モデル)
SQL Server固有の重要な概念がRecovery Modelです。ログの保持方針を決定します。
| Recovery Model | ログの切り捨て | PITR | 用途 |
|---|---|---|---|
| SIMPLE | Checkpoint時に自動切り捨て | 不可 | 開発/テスト環境 |
| FULL | ログバックアップ時に切り捨て | 可能 | 本番環境 |
| BULK_LOGGED | ログバックアップ時に切り捨て(一括操作は最小ログ) | 制限あり | 大量データロード時 |
-- 確認
SELECT name, recovery_model_desc FROM sys.databases;
-- 変更
ALTER DATABASE MyDB SET RECOVERY FULL;他のDBとの対応:
| SQL Server | Oracle | PostgreSQL | InnoDB |
|---|---|---|---|
| SIMPLE | NOARCHIVELOGモード | wal_level=minimal | ― |
| FULL | ARCHIVELOGモード | wal_level=replica + アーカイブ | binlog有効 |
FULLモデルでログバックアップを取らないと、ログファイルが際限なく肥大化します。これはSQL Server運用で最もよくあるトラブルの1つです。
まとめ
| トピック | InnoDB | PostgreSQL | Oracle | SQL Server |
|---|---|---|---|---|
| 名称 | REDO Log | WAL | REDO Log | Transaction Log |
| UNDO | 別ファイル | テーブル内 | 別領域 | 同じログ内 |
| ファイル管理 | 固定ファイル循環 | セグメント作成/削除 | グループ循環 | VLF(ファイル内論理区画) |
| 書き込み主体 | 専用スレッド | 各バックエンド | LGWR | Log Writer |
| Checkpoint | 軽い | 重い | Incremental | Indirect(継続的) |
| ログ保持方針 | ― | wal_level + アーカイブ | ARCHIVELOG | Recovery Model |
SQL Serverの特徴は、REDO/UNDOを1つのログに統合している点と、VLFによるファイル内論理管理です。Indirect Checkpointの導入により、OracleやInnoDBと同様にI/Oの平準化が実現されています。
次回はDb2 Transaction Logを解説し、最後にシリーズ全体の比較表でまとめます。