排他制御とデッドロックとは ― ロック待ちと解消
排他制御は、
排他制御は、ロックの組み合わせで待たせるかどうかを決める
排他制御とは、複数のトランザクションが同時に同じデータを更新しても、矛盾が起きないようにする仕組みです。後から来た側を待たせるかどうかは、2つのトランザクションが使うロックの種類の組み合わせで決まります。待たせ方によっては、互いに待ち合って動けなくなるデッドロックも起きます。
専有ロック(書き換えるときに取るロック)と共有ロック(読むときに取るロック)の基本と、ロックを取る範囲の大きさを表すロック粒度は、トランザクションとACID特性の記事で説明しています。
専有ロックと共有ロックの両立性
専有ロックと共有ロックには、同時に許可される組み合わせと、許可されない組み合わせがあります。同時に許可されるかどうかを、ロックの両立性と呼びます。次の図では、同じ1行のデータにトランザクションAが先にロックを取り、トランザクションBが後から要求する場面で、4つの組み合わせを比べます。
共有ロックどうしは、同時に許可されます。どちらも読むだけの処理のため、同時に進めても矛盾しません。一方、専有ロックが関わる組み合わせは、同時に許可されません。先にロックを持つ側の処理が終わるまで、もう一方は待たされます。同時に動くトランザクションに、ほかの変更がどこまで見えるかの段階は、分離レベルの記事で説明しています。
デッドロックは、互いのロックの解放を待ち合う状態
デッドロックとは、複数のトランザクションが、互いの持つロックの解放を待ち合い、どちらも先に進めなくなる状態です。原因は、それぞれのトランザクションがロックを取る順番の食い違いにあります。例えば、注文表と在庫表という2つの表を、トランザクションAとBがそれぞれ更新する場面を考えます。
Aが注文表から在庫表の順でロックを取り、Bが在庫表から注文表の順でロックを取ると、互いに相手がロックした表を取りにいく形になります。次の図で、ロックを取る順番を切り替えると、デッドロックになる場合とならない場合を確かめられます。
ロックを取る順番が食い違うと、Aは在庫表の解放を待ち、Bは注文表の解放を待つので、どちらも終わりません。順番をそろえると、Bは単にAの完了を待つだけになり、デッドロックは起きません。
デッドロックは、検知後にDBMSが一方をロールバックして解消する
デッドロックは、待っているだけでは解消しません。MySQLやPostgreSQLは、デッドロックの状態を検知すると、関係するトランザクションの一方を自動的にロールバックします。どちらをロールバックするかはDBMSが決めるため、アプリケーションの側では選べません。
ロールバックされたトランザクションは、もう一度やり直す必要があります。そのため、アプリケーションは、自分の側がロールバックされる場合に備えて作る必要があります。デッドロックの危険を減らすには、すべてのトランザクションでロックを取る順番をそろえる対策が有効です。先にロックを取るやり方と、ロックを取らずに更新の時点で衝突を見つけるやり方の違いは、楽観ロックと悲観ロックの記事で比べています。
参考にした資料
- MySQL 8.4 Reference Manual「InnoDB Locking」(共有ロック・専有ロックの両立性)
- MySQL 8.4 Reference Manual「Deadlocks in InnoDB」(デッドロック検知とロールバック)
- PostgreSQL 16 日本語ドキュメント「13.3. 明示的ロック」(デッドロックの定義、検知後の解決、ロック順序をそろえる対策)
練習問題(全5問)
正解:即座に
共有ロックは、
正解:先の
専有ロックは、
正解:複数の
デッドロックとは、
正解:すべての
すべての
正解:関係する
MySQLや
選んでから「採点する」を押すと、解説つきで答え合わせができます。
関連記事
おすすめの本 PR
セキュリティとデータベースは、基本情報技術者試験の対策本でまとめて学べます。

