MySQLのロック機構とMVCCの原理
ロックは並行処理制御の中核をなすものです。ロックの仕組みを理解してこそ、効率的な並行処理コードを書くことができるのです。
このレッスンでは、MySQLのロック機構について解説します。
1. 学習内容
- テーブルロックと行ロック
- 共有ロック(Sロック)と排他ロック(Xロック)
- インテントロック
- MVCC(マルチバージョン並行制御)
- デッドロックの検出と対処
2. 錠の種類
graph TB
A[MySQL Locks] --> B[By Granularity]
A --> C[By Type]
B --> D[Table Lock Table Lock]
B --> E[Row Lock Row Lock]
B --> F[Page Lock]
C --> G[Shared Lock S Lock]
C --> H[Exclusive Lock X Lock]
C --> I[Intent Lock Intention Lock]
| ディメンション | テーブルロック | 行ロック |
|---|---|---|
| 粒度 | テーブル全体 | 1行 |
| 衝突の可能性 | 高 | 低 |
| 並行処理のパフォーマンス | 低 | 高 |
| コスト | 低 | 高 |
| エンジン | MyISAM/InnoDB | InnoDB |
3. 共有ロックと排他ロック
(1) 共有ロック (Sロック)
SQL
-- Acquire a shared lock (read lock) — MySQL 8.0+ Recommended Syntax
SELECT * FROM users WHERE id = 1 FOR SHARE;
-- MySQL 5.7 old syntax (8.0 deprecated but still usable)
-- SELECT * FROM users WHERE id = 1 LOCK IN SHARE MODE;
-- Other transactions can read, but cannot write
(2) 排他ロック(Xロック)
SQL
-- Set an exclusive lock (write lock)
SELECT * FROM users WHERE id = 1 FOR UPDATE;
-- Other transactions cannot read (if also locked) or write
| ロックタイプ | 読み取り | 書き込み |
|---|---|---|
| 共有ロック S | ✅ | ❌ |
| 専用ロック X | ❌ | ❌ |
4. インテントロック
インテントロックとは、テーブル内の行がロックされているかどうかを迅速に判断するために使用される、テーブルレベルのロックです。
| インテントロック | 説明 |
|---|---|
| IS (Intent Shared Lock) | トランザクションは、その行に S ロックを掛けることを意図している |
| IX (Intent Exclusive Lock) | トランザクションは、その行に X ロックを掛けることを意図しています |
目的: テーブルロックを取得する際、各行を個別にチェックする必要はなく、対象となるロックのみを確認すればよい。
▶ 例:ロックの実際の活用例
SQL
-- Session 1: Set an exclusive lock
START TRANSACTION;
SELECT * FROM accounts WHERE id = 1 FOR UPDATE;
-- At this moment id=1 is locked
-- Session 2: Try updating (Waiting)
UPDATE accounts SET balance = 500 WHERE id = 1;
-- Wait until Session 1 commits or times out
-- Session 1: Commit
COMMIT;
-- Session 2 execution succeeds
5. MVCC(マルチバージョン並行制御)
MVCCは、データの複数のバージョンを保持することで、ロックフリーの読み取りを可能にします。
(1) MVCCの仕組み
graph LR
A[Current Data] --> B[Undo Log<br/>History of Versions]
B --> C[v1] --> D[v2] --> E[v3]
F[Read View] -->|Based on the quarantine level| G[Which version to read?]
(2) さまざまな分離レベルにおける読み取り動作
| 隔離レベル | 読み取りモード |
|---|---|
| 未コミットデータの読み取り | 常に最新のデータを読み取る(ダーティリードとなる可能性がある) |
| 読み取りコミット | SELECT 文ごとに新しい読み取りビューを作成 |
| 繰り返し読み取り | トランザクションの開始時に読み取りビューを作成する |
| シリアライズ可能 | ロック付き読み取り |
6. デッドロック
(1) デッドロックとは何か?
2つのトランザクションが、互いにロックを解放するのを待っています:
SQL
-- TransactionsA
START TRANSACTION;
UPDATE accounts SET balance = 100 WHERE id = 1; -- Lock id=1
UPDATE accounts SET balance = 200 WHERE id = 2; -- Waiting for id=2
-- Transaction B
START TRANSACTION;
UPDATE accounts SET balance = 300 WHERE id = 2; -- Lock id=2
UPDATE accounts SET balance = 400 WHERE id = 1; -- Waiting for id=1 (Deadlock!)
(2) デッドロックの処理
SQL
-- View deadlock information
SHOW ENGINE INNODB STATUS\G
-- Set the lock wait timeout (seconds)
SET innodb_lock_wait_timeout = 50; -- seconds
(3) デッドロックを回避する
| 戦略 | 説明 |
|---|---|
| 固定順序 | テーブルと行を同じ順序で参照する |
| ロックの保持時間を短縮する | トランザクションをできるだけ早くコミットする |
| より低い隔離レベルを使用する | READ COMMITTED ロックの数を減らす |
| インデックスを賢く作成する | ロックの範囲を縮小する |
7. ロック待機タイムアウト
SQL
-- View Lock Wait Timeout
SELECT @@innodb_lock_wait_timeout;
-- Settings (seconds)
SET innodb_lock_wait_timeout = 10;
-- Timeout Error
-- ERROR 1205: Lock wait timeout exceeded
❓ よくある質問
Q InnoDB はテーブルロックと行ロックのどちらを使用しますか?
A デフォルトでは、行ロック(インデックスベース)を使用します。インデックスが存在しない場合は、テーブルロックに切り替わります。
Q SELECT文はロックを取得しますか?
A 通常のSELECT文はロックを取得しません(MVCCスナップショット読み取り)。
SELECT ... FOR UPDATEは排他ロックを取得し、SELECT ... FOR SHARE(MySQL 8.0以降)は共有ロックを取得します。レガシー構文であるLOCK IN SHARE MODEは、8.0で非推奨となっています。Q 現在のロックを確認するにはどうすればよいですか?
A
SELECT * FROM performance_schema.data_locks; (MySQL 8.0)Q InnoDB はテーブルロックと行ロックのどちらを使用しますか?
A デフォルトでは、インデックスに基づいて実装された行ロックを使用します。インデックスが存在しない場合は、テーブルロックに切り替わります。
Q SELECT文はロックを取得しますか?
A 通常のSELECT文はロックを取得しません(MVCCスナップショット読み取り)。FOR UPDATEを指定すると排他ロックを取得し、FOR SHAREを指定すると共有ロックを取得します。
📖 まとめ
- テーブルロックは粒度が粗く、同時実行性が低い一方、行ロックは粒度が細かく、同時実行性が高い。
- 共有ロック (S) では複数の読み取りが可能ですが、排他ロック (X) では排他的な書き込みのみが可能です
- MVCC は、マルチバージョン並行制御を通じてロックフリーの読み取りを可能にします
- デッドロックは、プロセス同士が互いに待機している状態が発生したときに生じます。MySQLはこれを自動的に検出し、いずれかのトランザクションをロールバックします。
- デッドロックを回避する:アクセス順序の固定、ロック保持時間の短縮
📝 練習問題
-
基本問題(難易度:⭐):共有ロックと排他ロックの違いを説明してください。
-
上級演習(難易度:⭐⭐):デッドロックが発生するシナリオを作成し、MySQLがそれをどのように処理するかを観察してください。
-
チャレンジ問題(難易度:⭐⭐⭐):異なる分離レベルにおける並行読み取りおよび書き込みの挙動を比較してください。



