今回は「MySQLにおける排他制御(Concurrency Control)とトランザクションの分離レベル」について、初学者でもわかるように、例えや表を交えて丁寧に解説していきます。
排他制御とは?その目的は?
複数の人(トランザクション)が同時に同じデータにアクセスしようとすると、思わぬバグやデータの矛盾が起きてしまうことがあります。
例えるなら、「ひとつのノートに、同時に別の人が書き込もうとする」状態です。
うまく調整しないと、メモが重なったり、消されたりしてしまいます。
そこで登場するのが排他制御です。
MySQLでの排他制御の方法
方法①:ロック(Lock)
ロックは「このデータ、今自分が使ってるよ!」と周囲に伝える仕組みです。
ロックの種類
種類 | 概要 | 他のトランザクションの影響 |
---|---|---|
共有ロック(Shared Lock) | データの読み取りを許可 | 書き込みはできない |
排他ロック(Exclusive Lock) | 読み取り・書き込みを独占 | 他の読み書きをブロック |
明示的なロックの例
START TRANSACTION;
SELECT * FROM items WHERE id = 1 FOR UPDATE; -- 排他ロック
-- ここで更新処理などを行う
COMMIT;
FOR UPDATE
を使うと、その行に対して他の更新をブロックできます。
方法②:トランザクションの分離レベル(Isolation Level)
4つの分離レベルと特徴
レベル | Dirty Read | Non-Repeatable Read | Phantom Read | 同時実行性 |
---|---|---|---|---|
READ UNCOMMITTED | あり | あり | あり | 高い |
READ COMMITTED | なし | あり | あり | やや高い |
REPEATABLE READ(デフォルト) | なし | なし | あり | 中程度 |
SERIALIZABLE | なし | なし | なし | 低い |
- Dirty Read:他の未確定データを読んでしまう
- Non-Repeatable Read:同じクエリ結果が2回で異なる
- Phantom Read:検索条件を満たす行の数が変わる
設定方法
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
※ トランザクション開始前に設定してください!
デッドロックとは?
複数のトランザクションが、互いにロックを待ち合ってしまい、永久に進めない状態です。
例えるなら…
- Aさんがノート1を持ってノート2を待っている
- Bさんがノート2を持ってノート1を待っている
→ どちらも譲らない、膠着状態になる
MySQLはこの状態を検出して、自動的に**どちらかを強制終了(ROLLBACK)**させます。
対策:
- ロックの順序を統一する
- トランザクションはできるだけ短く
- 必要な行だけにロックをかける
実例:排他ロックでデータ競合を防ぐ
シナリオ
- テーブル:
inventory
- カラム:
id
,stock
目的:在庫を更新する前にロックして他の人の更新を防ぐ
START TRANSACTION;
SELECT stock FROM inventory WHERE id = 1 FOR UPDATE;
-- 在庫数に応じた処理を行う
UPDATE inventory SET stock = stock - 1 WHERE id = 1;
COMMIT;
解説
FOR UPDATE
:排他ロックをかけて他の更新をブロックCOMMIT
:処理が完了したら確定
まとめ
項目 | 内容 |
---|---|
排他制御の目的 | 同時実行でもデータの整合性を保つ |
方法1:ロック | 読み書きの競合を避ける |
方法2:分離レベル | 読み取りの一貫性を保証 |
デフォルトの分離レベル | REPEATABLE READ(非反復読み取りを防止) |
デッドロック | ロックの待ち合い状態、MySQLが自動解決するが注意が必要 |
今後の学習の指針
- 実際にトランザクションと
FOR UPDATE
を使ってデモしてみよう - 分離レベルごとに、どんな問題が起きるかシミュレーションして比較
SHOW ENGINE INNODB STATUS
を使ってデッドロックの発生状況を確認してみよう- ORMやフレームワーク(JPA, MyBatis)での排他制御の書き方もチェックしよう
データベースの整合性を守る力=プロフェッショナルエンジニアの証です。
焦らず、順番にステップアップしていきましょう!