1. MVCC、ロックベースとは
MVCC(Multi-Version Concurrency Control, 多版本並行制御) は「読み取りは過去バージョンを参照することでロックを不要にする方式」です。SELECT文等の参照では過去バージョンを読むことでロック競合を避けます。
MVCCを採用している代表的なデータベースはOracle, PostgreSQL, MySQL等があります。
ロックベース(Two-Phase Locking; 2PL, 二相ロック方式) は「読み取りと更新と共に必ずロックを取得し、コミットまで保持する」方式です。ロックで整合性を保持します。
ロックベースを採用している代表的なデータベースはHiRDB, DB2, SQL Server(既定モード)等があります。
SQL Serverは既定モードではロックベースですが、設定を変更してMVCCとすることもできます。
2. MVCC と ロックベースの違い
MVCC と ロックベース の主な違いは以下の通りです。
2.1. 読み取りの扱いの違い
- MVCC
- SELECT はロックを取らない
- Undo や行バージョンを参照して「過去の一貫した状態」を読む
- 読み取りと更新が競合しない
- ロックベース
- SELECT でも共有ロックを取得
- 更新は共有ロックと競合する
- 読み取りと更新がブロックし合う
2.2. 更新の違い
- MVCC
- 更新すると「新しいバージョン」を作る
- 古いバージョンは他トランザクションのために残す
- ロックベース
- 更新対象の行に排他ロックを取得
- コミットまで他トランザクションはその行を読めない
2.3. 並行性(スケーラビリティ)
- MVCC
- 読み取りと書き込みが干渉しないため並行性が高い
- 高負荷 OLTP に強い
- ロックベース
- 読み取りと書き込みが競合するため並行性が低い
- ロック待ち・デッドロックが発生しやすい
2.4. 一貫性の保証方法
- MVCC
- スナップショットで一貫性を保証
- 分離レベルによっては「読み取りの揺れ」が起きる(READ COMMITTED など)
- ロックベース
- ロックで厳密に整合性を保証
- 読み取りの揺れは起きないが、ロック待ちが増える
2.5. まとめ( MVCCとロックベースの違い)
| 観点 | MVCC | ロックベース |
|---|---|---|
| SELECTのロック | 取らない | 共有ロックを取る |
| 読み取りと更新の競合 | しない | する |
| 更新方式 | 新バージョン作成 | 排他ロックで更新 |
| 並行性 | 高い | 低い |
| 読み取りの一貫性 | Undo/スナップショット依存 | ロック依存 |
| ダーティーリード | 原則不可 | 分離レベル次第 |
| デッドロック | 少ない | 多い |
| 向いている用途 | 高並行 OLTP | 厳密整合性が必要な処理 |
3. 主なMVCC型DBの違い
主なMVCC型のDBである Oracle、PostgreSQL、MySQL (InnoDB) の特徴は以下の通りです。
| DB | MVCCの実装方式 | 可視性判定の仕組み | 特徴・課題 |
|---|---|---|---|
| Oracle | Undoセグメント方式 | Undoに保存された過去イメージを参照 | SELECTは常にUndoから一貫性読み取り。Undo管理が複雑で、長時間トランザクションでUndo肥大化の課題あり。 |
| PostgreSQL | 行バージョン(Tuple)方式 | xmin/xmaxフィールド+スナップショット | 更新は新しい行を挿入し、旧行に削除マーク。不要バージョンはVACUUMで回収。シンプルだがVACUUM負荷やXIDラップアラウンド問題が課題。 |
| MySQL (InnoDB) | Undoログ+Gap Lockのハイブリッド方式 | Undoログ+ReadView+Gap Lock | Undoで過去バージョンを参照しつつ、Gap Lockで範囲挿入を制御。ファントムリードを物理的に防ぐが、Gap Lockによる競合が増える場合あり。 |
- 共通点:読み取りはロックを取らず、過去バージョンを参照する
- 違い:Undo依存か、行バージョン依存か、Gap Lockを併用するか、の違いがある
4. 主なロックベース型DBの違い
主なロックベース型のDBである HiRDB、DB2、SQL Server の特徴は以下の通りです。
| DB | ロック方式 | 分離レベル対応 | 特徴・課題 |
|---|---|---|---|
| HiRDB | 強制ロック方式 | READ UNCOMMITTEDなし | SELECTでも共有ロック必須。基本的にダーティーリード不可(例外あり)。整合性最優先だが並行性は低め。 |
| DB2 | 標準的な2PL(二相ロック) | SQL標準の4分離レベルすべて対応 | READ UNCOMMITTEDも選択可能。ロック粒度は行/ページ/テーブル。柔軟性が高い。 |
| SQL Server | 2PL+オプションMVCC | デフォルトはREAD COMMITTED(ロックベース) | RCSIやSnapshot Isolationを有効化するとMVCC風に動作。並行性と整合性のバランスを選べる。 |
- 共通点:ロックで整合性を保証する
- 違い:HiRDBは常に強制ロック、DB2は柔軟、SQL Serverはロック+MVCCを選べる
5. Oracle、PostgreSQL、MySQL(InnoDB)、HiRDB、DB2、SQL Serverの違い(まとめ)
-
MVCC型 は「読み取りは過去バージョンを参照」するが、実装方式が違う
- Oracle:Undo依存
- PostgreSQL:行バージョン依存
- MySQL(InnoDB):Undo+Gap Lockのハイブリッド
-
ロックベース型 は「ロックで整合性を保証」するが、制御の強さが違う
- HiRDB:常に強制ロック、整合性最優先
- DB2:標準的な2PL、柔軟に分離レベル選択可能
- SQL Server:ロックベースだがMVCCも選べる
- MVCCは並行性を重視しつつ方式が分かれる。
- ロックベースは整合性を重視しつつ制御の強さが分かれる。
6. MVCC型のOracleとロックテーブル型のHiRDBのSELECT文の違い
-
OracleのSELECT(MVCC方式)
- 基本挙動
- SELECTはロックを取得しない
- 過去のスナップショット(Undoセグメント)を参照して一貫性を保つ
- 他トランザクションが更新中でも、コミット済みの過去バージョンを読むためブロックされない
- 結果
- 読み取りと更新が競合しない → 高並行性
- ダーティーリードは起きない(未コミットデータは見えない)
- ただし分離レベルがREAD COMMITTEDだと「非再現読み取り」「ファントムリード」は起き得る
- 基本挙動
-
HiRDBのSELECT(ロックベース方式)
- 基本挙動
- SELECTはデフォルトでWITH SHARE LOCKが仮定さる
- 検索した行に共有ロックがかかり、他トランザクションは更新できない
- WITHOUT LOCKを指定すればロックを取らずに参照できるが、更新中の行は待機となる(または未コミット読み取りが発生する)
- 結果
- 読み取りと更新が競合する → 並行性は低い
- 基本的にダーティーリードは起きないが、共有ロックをかけないと起きる場合もある
- 整合性は強いが、ロック待ちやデッドロックが発生しやすい
- 基本挙動
HiRDBのSELECT文で、ロックを取得しない場合、WITHOUT LOCKを付与します。
WITHOUT LOCK NOWAITを付与すると、対象行が他トランザクションで更新中(排他ロック中)の場合は、未コミット読み取り(ダーティリード相当)が発生する可能性があります。
WITHOUT LOCK WAITを付与すると、対象行が他トランザクションで更新中(排他ロック中)の場合は、更新が終わるまで待機してから参照します。
OracleではUndo領域からコミット済みの過去バージョンを読むためエラーや待ちが発生せずに読むことができます。
| 観点 | Oracle (MVCC) | HiRDB (ロックベース) |
|---|---|---|
| SELECT時のロック | 取得しない | デフォルトで共有ロック |
| 未コミットデータ参照 | 不可(Undoで防止) | 不可(共有ロックで防止) |
| 読み取りと更新の競合 | しない | する |
| ダーティーリード | 起きない | 起きない(ロックで防止) |
| 非再現読み取り | 起き得る(READ COMMITTEDの場合) | 起きない(ロックで防止) |
| ファントムリード | 起き得る(READ COMMITTEDの場合) | 起きない(ロックで防止) |
| 並行性 | 高い | 低い |
-
Oracle:SELECTはロックを取らず、Undoで過去バージョンを読む → 並行性が高いが読み取りの揺れは起き得る
-
Oracleは「並行性重視」
-
Oracleは「並行性重視」
-
HiRDB:SELECTはデフォルトで共有ロック → 並行性は低いが読み取りの揺れは起きない
- HiRDBは「整合性重視」
以上