404 Not Found

404 Not Found


nginx

MySQLのロック機構とMVCCの原理

ロックは並行処理制御の中核をなすものです。ロックの仕組みを理解してこそ、効率的な並行処理コードを書くことができるのです。

このレッスンでは、MySQLのロック機構について解説します。

1. 学習内容


2. 錠の種類

100%
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の仕組み

100%
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を指定すると共有ロックを取得します。

📖 まとめ


📝 練習問題

  1. 基本問題(難易度:⭐):共有ロックと排他ロックの違いを説明してください。

  2. 上級演習(難易度:⭐⭐):デッドロックが発生するシナリオを作成し、MySQLがそれをどのように処理するかを観察してください。

  3. チャレンジ問題(難易度:⭐⭐⭐):異なる分離レベルにおける並行読み取りおよび書き込みの挙動を比較してください。

Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%