Breaking News

Default Placeholder Default Placeholder

この記事は何?

本記事ではMySQLのデータベースでわざとデッドロックを起こす方法およびこういうことなんだってことを簡単に解説します。

書くこと書かないこと

書く

  • デッドロックの説明
  • デッドロックを引き起こす処理の流れ
  • 回避する方法(のごく一部)

書かない

  • MySQLの実行環境の構築方法
  • テーブル作成方法
  • クエリの詳細な説明

デッドロックとは

デッドロックとは、複数のトランザクションがお互いに操作対象のデータをロック(触れないように)し、お互いがお互いのロック解除を永遠に待ち続ける悲しい状態です。

デッドロックを起こしてみる

実際にデッドロックを起こしてみましょう。

2人が同時に2つのテーブルに対して操作を試行する、というケースを考えてみます。

Aさん

  • テーブル sample を更新する
  • テーブル sample2を更新する

Bさん

  • テーブル sample2 を更新する
  • テーブル sample を更新する

実行するクエリはこんな感じです。実行される順番は各クエリの末尾に書いた数字順になります。

# Aさん

> BEGIN; --- ①
> UPDATE sample SET name = "John" WHERE id = 1; --- ③
> UPDATE sample2 SET name = "Windows" WHERE id = 1; --- ⑤
> COMMIT; --- ⑦

# Bさん
> BEGIN; --- ②
> UPDATE sample2 SET name = "Mac" WHERE id = 1; --- ④
> UPDATE sample SET name = "Sarah" WHERE id = 1; --- ⑥
> COMMIT; --- ⑧

数字の順番に実行していくと、 ⑥でデッドロックが発生します。エラーはこんなのが出力されます。

ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction

回避方法

これを回避するには以下のような方法があります。

クエリの実行順序に一貫性を持たせる

クエリの実行順序が同じであればデッドロックは発生しません。

先ほど実行したクエリをこのようにします。

samplesample2の操作順番を揃えています。

# Aさん

> BEGIN; --- ①
> UPDATE sample SET name = "John" WHERE id = 1; --- ③
> UPDATE sample2 SET name = "Windows" WHERE id = 1; --- ⑤
> COMMIT; --- ⑦

# Bさん
> BEGIN; --- ②
> UPDATE sample SET name = "Sarah" WHERE id = 1; --- ④
> UPDATE sample2 SET name = "Mac" WHERE id = 1; --- ⑥
> COMMIT; --- ⑧

④を実行したときに待ちが発生しますが、これだとデッドロックは発生しません。

しかし実際の開発プロジェクトで必ずしも処理の順番を揃えることができるとは限りません。その際は別の手法で回避する必要があります。

トランザクションを直列に行う

トランザクションが必ず直列に行うように設定することで、 同時にデータ操作が行われることを回避します。

トランザクションを開始する前に、以下を実行してください。

SET autocommit=0;

これで準備OKです。

以下のクエリを順番に実行してみましょう。

> BEGIN; --- ①
> UPDATE sample SET name = "John" WHERE id = 1; --- ③
> UPDATE sample2 SET name = "Windows" WHERE id = 1; --- ⑤
> COMMIT; --- ⑦

# Bさん
> BEGIN; --- ②
> UPDATE sample2 SET name = "Mac" WHERE id = 1; --- ④
> UPDATE sample SET name = "Sarah" WHERE id = 1; --- ⑥
> COMMIT; --- ⑧

順番に実行していくと、④で待ちが発生します。これは、データ更新処理は1度に1つしかダメですよ。前の処理がコミットされるまで待ってください。ということが起きています。

つまり、これでデッドロックは絶対に発生しません。

しかし、これだと大量のリクエストがあった場合に多数の待ちが発生してしまいます。応答性が悪くなり、ユーザから苦情が来てしまうでしょう。

最後に

今回の記事はこれで終わりです。

デッドロックに関しては絶対的な回避方法がない、というのが答えなのです。トランザクションを辞めればいい、というのも1つの手でしょう。しかしそうするとデータの一貫性がなくなり、問題があった場合にデータ不整合が生じてしまいます。。。

デッドロックと人類の戦いは終わらない。