1. ホーム
  2. データベース
  3. その他のデータベース

5分でわかる!データベースのデッドロックのシナリオと解決策

2022-01-18 17:49:50

前書き

ロックは、データベースへの同時アクセス時にデータの一貫性と整合性を確保するための主要なメカニズムです。どのトランザクションもデータにアクセスするために対応するオブジェクトのロックを取得する必要があり、データを読み取るトランザクションは通常読み取りロック(共有ロック)だけを取得すればよく、データを変更するトランザクションは書き込みロック(排他ロック)を取得する必要があります。2つのトランザクションが互いに取得したリソースを解放するのを待つ必要がある場合、システムが介入しなければ永遠に待つことになり、デッドロック状態に入ることを意味する。

<ブロッククオート

以下は、Oracle、MySQL、Microsoft SQL Server、PostgreSQLなど、一般的なデータベース管理システムに適用されるものです。

デッドロックはどのように発生するのですか?

デッドロック発生のデモは非常に簡単で、2行のデータを持つ簡単な例のテーブルを作成するだけです。

CREATE TABLE t_lock(id int PRIMARY KEY, col int);
INSERT INTO t_lock VALUES (1, 100);
INSERT INTO t_lock VALUES (2, 200);

SELECT * FROM t_lock;
id|col|
---+--+
 1|100|
 2|200|


異なるトランザクションのデータを異なる順序で変更すると、トランザクション同士が待ち状態になることがあります。あるトランザクションが他のトランザクションのリソースを解放するのを待つのは問題ありませんが、2つのトランザクションが互いのリソースを待つ場合、データベース管理システムには、無期限に待つか、一方のトランザクションを中断して他方を正常に実行させるかの2つの選択肢しかありません。

無限に待つのは明らかに答えにならないので、データベースは通常、一定時間待った後、どちらかのトランザクションを中止させる。

以下は、デッドロックのデモ例です。

<テーブル トランザクションI トランザクション2 備考 BEGIN BEGIN 2つの別々のトランザクションを開始する UPDATE t_lock
SET col = col + 100
WHERE id = 1; UPDATE t_lock
SET col = col + 200
WHERE id = 2; トランザクション1はid=1のデータを変更し、トランザクション2はid=2のデータを変更する UPDATE t_lock
SET col = col + 100
WHERE id = 2; トランザクション 1 は id=2 のデータを変更し、トランザクション 2 が書き込みロックを解放するのを待つ必要があります。 待機中... UPDATE t_lock
SET col = col + 200
WHERE id = 1; トランザクション2はid=1のデータを変更し、トランザクション1が書き込みロックを解放するのを待つ必要があります。 デッドロック デッドロック データベースがデッドロックを検出し、トランザクションの中断を選択した場合。 更新成功 リターンエラー

MySQL InnoDBの場合、innodb_deadlock_detectオプションがデフォルトで有効になっていると、トランザクション2が以下のエラーメッセージを返します。

エラー 1213 (40001)。ロックを取得しようとしたときにデッドロックが見つかったので、トランザクションを再開してください。

InnoDBデッドロック検出オプションを無効にすると、トランザクション2は50秒後に待ちタイムアウトを促します(innodb_lock_wait_timeout)。

<ブロッククオート

エラー 1205 (hy000)。ロック待ちのタイムアウトを超えましたので、トランザクションを再開してください。

Oracleはデッドロックを検出すると、以下のエラーを返します。

ORA-00060: リソースの待ち時間にデッドロックを検出しました

デッドロックが検出されたときに Microsoft SQL Server が返すエラーは、次のとおりです。

メッセージ1205、レベル13、ステータス51、行7
このトランザクション(プロセスID 67)はロックリソースで他のプロセスとデッドロックしており、デッドロックの犠牲者として選択されています。トランザクションを再実行してください。

PostgreSQLがデッドロックを検出した際に返すエラーは以下の通りです。

SQL エラー [40P01]: エラーです。デッドロックが検出されました
  詳細 プロセス 32 がトランザクション 4765 の ShareLock を待機中です(プロセス 16552 によってブロックされています)。
プロセス 16552 はトランザクション 4766 で ShareLock を待機中で、プロセス 32 によってブロックされています。
  提案します。詳細については、サーバーログを参照してください。
  位置:関係 "t_lock" のタプル (0, 1) を更新するとき。

デッドロックの解消と回避方法

デッドロックは、データベース自体の問題ではないので、データベースの構成を最適化しても解決や回避はできず、アプリケーションを修正することでしか解決できません。簡単に言えば、お互いのリソースを待つような状況を作らないために、アプリケーションの中で同じ順番でデータを修正する必要があるのです。例えば

<テーブル トランザクションI トランザクション2 備考 BEGIN BEGIN 2つの別々のトランザクションを開始する UPDATE t_lock
SET col = col + 100
WHERE id = 1;
UPDATE t_lock
SET col = col + 200
WHERE id = 1; トランザクション1とトランザクション2の両方がid=1のデータを変更し、後のトランザクションは待つ必要があります。 UPDATE t_lock
SET col = col + 100
WHERE id = 2; 待機中... トランザクション 1 は id=1 のデータを変更、トランザクション 2 は待機中 コミットします。 待機中... トランザクション1がコミット UPDATE t_lock
SET col = col + 200
WHERE id = 2; トランザクション 2 は id=2 のデータの変更を継続します。 コミットする。 トランザクション2コミット

上記のシナリオでは、デッドロックは発生しません。しかし、実際のアプリケーションでは、全く同じ順序でデータを変更できるとは限りません。どうしてもデッドロックが発生する場合は、システムが返すデッドロック例外をキャッチして、プログラムにリトライ機構を追加するのも解決策の一つです。

概要

この記事では、データベースのデッドロックの原因と解決策を簡単に紹介します。5分でわかる!データベースのデッドロックのシナリオと解決策に関する記事はこちらデータベースのデッドロックに関する詳しい情報は、過去の記事を検索するか、以下の記事を引き続き閲覧してください。