こう 聞かれます
- 「デッドロックが
起きたら、 どう 調べて どう 防ぎますか」 - 「デッドロックとは
何ですか。 ロックの 待ちとは 何が 違いますか」 - 「デッドロックの
エラーが 出た とき、 アプリは 何を すべきですか」
面接官が 見ている点
- 2 つの
トランザクションが、 互いの 持つロックを 待ち合う 形だと 説明できるか - DB の
記録から、 どの SQL が どの 順で ぶつかったかを 特定する 手順を 持っているか - ロックの
順番を そろえる、 トランザクションを 短く する、といった 根本の 手を言えるか - ゼロには
できないと 分かり、 やり直しの 作りまで 考えているか
短い答え
- デッドロックは、
2 つの トランザクションが 互いに 相手の 持つロックを 待ち、 どちらも 進めなくなる 状態です。 多くの DB は 自分で 見つけて、 片方を 取り消します。 - 調べる
ときは、 DB が 残すデッドロックの 記録から、 ぶつかった SQL と、 それぞれが どの 順で 何を ロックしたかを 特定します。 - 防ぐには、
ロックを 取る 順番を そろえ、 トランザクションを 短くします。 それでも 起こりうるので、 取り消された 側は トランザクションごとやり直す 作りにします。
この
少し 詳しい答え
どうして起きるか
在庫を 2 つ
- 注文 A の
トランザクションが、 商品 1 の 行を ロックする - 注文 B の
トランザクションが、 商品 2 の 行を ロックする - A が
商品 2 を ロックしようとして、 B を待つ - B が
商品 1 を ロックしようとして、 A を待つ
A は B を、
ただの
どう調べるか
DB はSHOW ENGINE INNODB STATUS の
- ぶつかった 2 つの
トランザクションが、 それぞれ流していた SQL - それぞれが、
どの 表の どの 行 (どの インデックス) の ロックを 持ち、 何を 待っていたか
次に、
どう防ぐか
- ロックを
取る 順番をそろえる 上の 例なら、 どの 注文も 商品の id の 小さい 順に 更新すれば、 輪が できません。 いちばん効く手です - トランザクションを短くする 中で
外の API を 呼んだり、 利用者の 入力を 待ったりしません。 ロックを 持つ 時間が 短いほど、 ぶつかる 機会が減ります - ロックする
行を減らす条件に 合う インデックスが 無いと、 DB は 多くの 行を 読んで ロックする ことがあります。 更新の 条件に 合う インデックスを 用意します - 最初に
まとめて ロックする 後で 更新する 行を、 トランザクションの 初めに 順番どおり SELECT ... FOR UPDATEでロックしておく 手もあります
SQL クエリ
-- どの注文も商品の id の小さい順にロックする
BEGIN;
SELECT id, stock FROM products
WHERE id IN (1, 2)
ORDER BY id
FOR UPDATE;
UPDATE products SET stock = stock - 1 WHERE id = 1;
UPDATE products SET stock = stock - 1 WHERE id = 2;
COMMIT;やり直しの作り
順番を
トレードオフ
ロックの
話す例は
よく ある間違い
- ロックの
待ちの 時間を 延ばして済ませる デッドロックは 待っても 終わりません。 輪が できる 順番を直します - 取り消された
トランザクションの、 途中の SQL だけをやり直す 取り消しで 前の SQL も 無かったことになっています。 トランザクションごとやり直します - トランザクションの
中で 外の API を呼ぶ ロックを 持つ 時間が 延び、 ぶつかる 機会が増えます
深掘りの問い
- 「デッドロックと
ロックの 待ちの 時間切れは、 どう 見分けますか」 デッドロックは DB が 待ち合いの 輪を 見つけた 時点で 片方を 取り消し、 専用の エラーに なります。 時間切れは 決めた 時間まで 待ってから 失敗し、 エラーも 別です、と答えます - 「分離レベルを
上げれば 防げますか」 上げると 取る ロックが 増え、 かえって 起きやすくなる ことがある、と答えます - 「ORM を
使っていると、 ロックの 順番が 見えにくいときは」実際に 流れる SQL を 記録に 出し、 更新の 順番を 確かめる、と答えます
