みなさんこんにちは masa です。今日はデータベースにおいて致命的なバグであるデッドロックについて取り上げます。プログラマーや DBA な方以外にも聞いたことがあるワードなのではないでしょうか。
開発の流れにおいて、きちんとトランザクション設計、リソースアクセス順序の設計がされていればデッドロックは理論上は発生しないはずなのですが、オープンソースのソフトウェア基盤なんかを使用している場合、世界中のエンジニアがバラで開発してる宿命というもあり、普通にデッドロックしたりしますw
デッドロックは同じタイミングでリソースを確保しにいった際に発生するものなので、リリースするまで発覚しなかったというケースもあります。
そもそもデッドロックとは何なのかおさらい
まずは問題ないアクセスについて図を描いてみましたのでみてみてください。トランザクション=スレッドと捉えてもらっても構いません。
(解説)上の図では各処理が同じ順番でテーブルへアクセスするため、仮に同タイミングで書き込みをしようとしてもどちらか遅れたほうが、相手の処理が終わる(=ロック解放)のを待つだけです。
では次は問題のある処理の図です。
(解説)上の図では各処理がテーブルへアクセスする順番が逆になっています。この場合にロックと次の処理が “たすき掛け” の状態になりますので、これ以上処理は進まず停止してしまいます。問題ない処理と決定的に違うのは、待ち時間が発生するのではなく、永久にストップするという点です。
どうすればデッドロックを無くせるのか?
これは実は非常に難しい質問だと思います。デッドロックはバグそのものですので、ソフトウェアの規模が大きくなれば大きくなるほど潜在的に増えていきます。小さいシステムならまだしも、数百万ステップに及ぶプログラムにおいて、人間ですから設計図がまちがっているかもしれませんし、プログラマーがうっかり実装を手誤ってしまうかもしれません。
個人的にはデッドロックはゼロに近づける(=減らす)努力はできても、完全にゼロにすることはほぼ不可能であるという立場にいます。ソフトウェアにおいてバグをゼロにすることは不可能であるという考えそのものですが、少しでもデッドロックが起きないように以下のことに気を付ける必要があります。
- トランザクションからアクセスするテーブルの順番を一貫する
- 不必要にトランザクションを長くしない
- 不必要に FOR UPDATE でロックを掛けない
- 可能な限り行ロックする
以上のことに気を付けて設計、実装が出来れば限りなくデッドロックを排除できると考えます。
それでもデッドロックが起きたときはどうすればいいのか
これは実装でカバーしましょう。トランザクションの処理にタイムアウトもしくはリトライ回数を準備しておき、どちらかの処理がロックを解放するようにします。
いかがでしたか?今日は発見が遅れがちなバグであるデッドロックについて取り上げました。あえて名前は挙げませんが世界的に有名なソフトウェアでもデッドロックは起きています。「デッドロック起こすのは初心者!」などとおごらずに慎重にプロジェクトを進めるよう常に意識したいものです。
丁寧な解説をありがとうございます!