今回は「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 ReadNon-Repeatable ReadPhantom 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)での排他制御の書き方もチェックしよう

データベースの整合性を守る力=プロフェッショナルエンジニアの証です。
焦らず、順番にステップアップしていきましょう!

最後までお読みいただきありがとうございます。