こう 聞かれます
- 「トランザクションの
分離レベルと、 起きる 問題を 説明できますか」 - 「在庫が 1 個の
商品を、 2 人が 同時に 買おうと しました。 どう 守りますか」 - 「行ロックと
楽観ロックは、 どう 使い 分けますか」
面接官が 見ている点
- 分離レベルを、
「同時に 動く 処理どうしが、 互いの 途中を どこまで 見て よいか」と 一言で言えるか - 3 つの
読み取りの 問題を、例で 区別できるか - DB ごとに
既定の レベルと 動きが 違う、と 知っているか (PostgreSQL と MySQL) - 在庫のような
「読んでから書く」処理を、 行ロックか 楽観ロックで守れるか
短い答え
- 分離レベルは、
同時に 動く トランザクションどうしが、 互いの 途中の 変更を どこまで 見て よいかを 決める 段階で、 SQL の 標準では Read Uncommitted から Serializable までの 4 つが あります。 - レベルが
低いほど 速く 動けますが、 ダーティリード、 ノンリピータブルリード、 ファントムリードといった 読み取りの 食い 違いが 起きます。 - 在庫の
引き当てのように 「読んでから書く」処理は、 分離レベルだけに 頼らず、 行ロック ( SELECT ... FOR UPDATE) か、version の 列を 使う 楽観ロックで 守ります。
少し 詳しい答え
ACID (トランザクションの 4 つの
3 つの 読み取りの問題
- ダーティリードほかの
トランザクションが、 まだ コミットしていない 書き込みを 読んでしまう こと。 相手が 取り消すと、 無かった 値を 読んだことになります - ノンリピータブルリード (ファジーリードとも
言います) 同じ 行を 2 回読むと、 その間に ほかの トランザクションが コミットした 更新で、 値が 変わっていること - ファントムリード同じ
条件で 2 回読むと、 その間に 行が 足されたり消されたりして、 条件に 合う 行の 集まりが 変わっていること
さらに、
4 つのレベル
SQL の
ただし、
- PostgreSQL 既定は Read Committed です。
Read Uncommitted を 指定しても Read Committed と 同じ 動きに なり、 Repeatable Read でも ファントムリードは 起きません - MySQL (InnoDB) 既定は Repeatable Read です
なので、
在庫を守る
在庫が 1 個の
1 つ目の
SQL クエリ
BEGIN;
SELECT stock FROM items WHERE id = 7 FOR UPDATE;
-- stock が 1 以上なら
UPDATE items SET stock = stock - 1 WHERE id = 7;
COMMIT;FOR UPDATE を
2 つ目の
SQL クエリ
UPDATE items
SET stock = stock - 1, version = version + 1
WHERE id = 7 AND version = 3 AND stock > 0;
-- 更新した行が 0 なら、誰かが先に変えたので、やり直すか断るトレードオフ
- 分離レベルを上げる
食い 違いは 減りますが、 待ちや、 DB に よる 取り消し (直列化の 失敗) が 増え、 やり直しの 処理が要ります - 行ロック確実ですが、
取り合いが 多いと 待ちが 長くなり、 ロックの 順番に よっては デッドロック (互いに 相手を 待って 止まる こと) も 起きます。 同時に 同じ行を 触る ことが 多い 場面に向きます - 楽観ロック待たずに
済みますが、 ぶつかった ときに やり直すか 断る 処理が 要ります。 ぶつかる ことが 少ない 場面に向きます
在庫をWHERE stock > 0 を
話すときは、
よく ある間違い
- 3 つの
読み取りの 問題を 混ぜて覚える 同じ 行の 値が 変わるのが ノンリピータブルリード、 行の 集まりが 変わるのが ファントムリード、と分けます - どの DB も
同じ 既定だと思う PostgreSQL は Read Committed、 MySQL (InnoDB) は Repeatable Read が既定です - トランザクションで
囲めば 在庫は 守れると思う 既定の レベルでは、 読んでから 書く 間に 割り込まれる ことがあります。 行ロックか 楽観ロックを使います
深掘りの問い
- 「デッドロックを
減らすには」複数の 行を ロックする ときは、 いつも 同じ 順番で 取る、 トランザクションを 短く する、 DB が デッドロックで 取り消したらやり直す、と答えます - 「Serializable に
すれば 全部 解決しますか」 食い 違いは 防げますが、 取り消しが 増えるので、 取り消されたらやり直す 作りが 要る、と答えます - 「楽観ロックと
悲観ロック、 どちらを 選びますか」 同じ 行を 同時に 触る ことが 多いなら 悲観ロック、 少ないなら 楽観ロック、と答えます