はじめに
この記事では、IBM Db2のロック機構の内部実装を深掘りします。
シリーズ最終回です。Db2はSQL Serverと同じくロックエスカレーションを持ちますが、Lock Avoidanceという独自の最適化で実際のロック取得を大幅に削減しています。Db2 11.5を中心に解説します。
Lock Avoidance
MVCCの記事でも触れましたが、Db2の最も特徴的なロック機能です。
仕組み
コミット済みであることが明らかな行に対して、ロックの取得自体をスキップします。
通常のロック:
1. 行を読む
2. ロックを取得
3. 処理
4. ロックを解放
Lock Avoidance:
1. 行のログ情報を確認
2. コミット済み? → YES → ロック不要、そのまま読む
→ NO → ロックを取得
-- Lock Avoidanceの効果を確認
db2 get snapshot for database on mydb | grep -i "lock avoidance"
Lock Avoidance = 98.5%ほとんどの行はコミット済みなので、実際にロックが必要になるケースは少数です。
ロックの粒度
Db2もSQL Serverと同じく複数の粒度を持ちます:
| 粒度 | 説明 |
|---|---|
| Table | テーブル全体 |
| Row | 行 |
SQL ServerのようなPage粒度はありません。
ロックエスカレーション
Db2もSQL Serverと同じくロックエスカレーションがあります。
閾値
-- ロックエスカレーションの閾値
db2 get db cfg for mydb | grep -i lock
Max storage for lock list (4KB) (LOCKLIST) = 4096
Percent of lock list before escalation (MAXLOCKS) = 22- LOCKLIST:ロックリスト全体のメモリサイズ
- MAXLOCKS:1つの接続がLOCKLISTの何%を使ったらエスカレーション
SQL Serverとの違い
| SQL Server | Db2 | |
|---|---|---|
| 閾値 | 約5,000行(固定的) | LOCKLIST × MAXLOCKS%(設定可能) |
| 無効化 | テーブル単位で可能 | LOCKSIZE TABLEで強制テーブルロック |
| パーティション単位 | AUTO設定で可能 | なし |
-- テーブルのロック粒度を変更
ALTER TABLE employees LOCKSIZE ROW; -- 行ロック(デフォルト)
ALTER TABLE employees LOCKSIZE TABLE; -- テーブルロック(エスカレーション不要)ロックモード
| モード | 略称 | 用途 |
|---|---|---|
| Intent None | IN | 無条件読み取り(UR) |
| Intent Share | IS | 読み取り(CS/RS) |
| Share | S | 読み取りロック |
| Intent Exclusive | IX | 更新予定 |
| Update | U | 更新ロック(デッドロック防止) |
| Exclusive | X | 書き込み |
Uロック(Update Lock)はSQL Serverにもある概念で、「これから更新する予定の行」にかけます。S→Xへの昇格時のデッドロックを防ぎます。
SKIP LOCKED / DATA CAPTURE
SKIP LOCKED
Oracleと同様に、ロック中の行をスキップできます:
SELECT * FROM jobs WHERE status = 'pending'
FOR UPDATE WITH RS USE AND KEEP UPDATE LOCKS
SKIP LOCKED DATA
FETCH FIRST 1 ROW ONLY;キュー処理パターンで有用です。
LOCK TIMEOUT
-- ロック待ちのタイムアウト(秒)
SET CURRENT LOCK TIMEOUT 10;
-- 即座にエラー(OracleのNOWAIT相当)
SET CURRENT LOCK TIMEOUT 0;
-- 無制限に待つ
SET CURRENT LOCK TIMEOUT -1;デッドロック検出
Db2もデッドロックを自動検出します。
-- デッドロック検出の間隔
db2 get db cfg for mydb | grep DLCHKTIME
Interval for checking deadlock (ms) (DLCHKTIME) = 10000| InnoDB | PostgreSQL | Oracle | SQL Server | Db2 | |
|---|---|---|---|---|---|
| 検出タイミング | 即座 | deadlock_timeout後 | 即座 | 即座 | DLCHKTIME間隔 |
| デッドロック時 | Tx全体 | Tx全体 | ステートメントのみ | Tx全体 | Tx全体 |
まとめ:ロック管理シリーズ全体比較
| トピック | InnoDB | PostgreSQL | Oracle | SQL Server | Db2 |
|---|---|---|---|---|---|
| 読み取りのロック | FOR SHARE時 | FOR SHARE時 | なし(CR Clone) | デフォルトでSロック | Lock Avoidanceで回避 |
| 行ロックの実装 | ロックテーブル | タプルヘッダ | ITL(ブロック内) | ロックマネージャ | ロックリスト |
| ロックエスカレーション | なし | なし | なし | あり | あり |
| Gap/Range Lock | Next-Key Lock | なし | なし | Key-Range Lock | なし |
| SERIALIZABLE | ロック | SSI | ロック | ロック | ロック |
| SKIP LOCKED | 8.0〜 | 9.5〜 | 古くからあり | あり | あり |
| デッドロック時 | Tx全体 | Tx全体 | ステートメントのみ | Tx全体 | Tx全体 |
| 独自機能 | Insert Intention Lock | Advisory Lock, SSI | ITL, NOWAIT/WAIT | NOLOCK, ロックヒント | Lock Avoidance |
5つのDBを比較すると、ロック設計は大きく2つの哲学に分かれます:
- ロックを最小化する派(Oracle、Db2):読み取りはロックを取らない。CR CloneやLock Avoidanceで回避
- ロックを活用する派(SQL Server):ロックで一貫性を保証。エスカレーションでメモリを管理
- MVCCで代替する派(InnoDB、PostgreSQL):MVCCで読み書きの競合を回避。書き込み同士のみロック