データ / DB 設計
正しく保ち、速く引く
テーブル設計は「同じ事実を2か所に書かない」ことから始まり、インデックスは「読む行を減らす」ためにある。そしてトランザクションの分離レベルは、同時に動く処理がお互いをどこまで見てよいかの取り決め。遅いクエリの原因は、たいていこの3つのどれか。
読み終わると、これに答えられるようになります
- ?複合インデックスの列の順番はなぜ重要なのか
- ?READ COMMITTED で起きて REPEATABLE READ で起きない異常は何か
- ?件数が増えたら急に遅くなったクエリの、最初に見る場所はどこか
正規化 — 同じ事実を2か所に書かない
正規化は理論ではなく「更新時に矛盾が起きない形」を作る作業。どこまで正規化し、どこで意図的に崩すか。
商品名を注文テーブルにも持たせた結果、同じ商品なのに表記が3種類に分裂した
インデックス — 読む行を減らすための索引
B-tree が何をしているかと、複合インデックスの列順がなぜ効くのか。張れば速くなるものではない。
遅いクエリに片っ端からインデックスを張り、書き込みが3倍遅くなった
トランザクションと分離レベル — 同時に動く処理が互いをどこまで見てよいか
在庫が二重に引かれる、残高がずれる。同時実行の事故は、分離レベルの選択と設計の両方で決まる。
在庫チェックと在庫更新の間に別の注文が入り、在庫1個の商品が2人に売れた
N+1 問題 — 1回で済むはずの問い合わせが100回になる
ORM の便利さが隠してしまう問い合わせ回数。発見の仕方と、対処の選択肢を使い分ける。
1件あたり2msのクエリが200回走り、一覧ページの表示に4秒かかっていた
実行計画 — DB が何をしようとしているかを読む
推測でチューニングしない。EXPLAIN の読み方と、見積もりと実測のズレが何を意味するか。
統計情報が古く、インデックスがあるのにフルスキャンが選ばれ続けていた
RDB と NoSQL — 問い合わせの形を、いつ決めるか
性能の比較ではなく「アクセスパターンがもう固まっているか、まだ動くか」で選ぶ。後から取り方を増やしたくなったとき、何が起きるかの違い。
取り方が固まる前に NoSQL を選び、後から来た検索要件が全件走査になった
マイグレーション — 流した瞬間に、書き込みが全部待つ
列を1つ足すだけの変更が、本番で11分サイトを止める。行数ではなく、同時に走っているトランザクションの長さが効く。
列を1つ足すだけの変更で、書き込みが11分間詰まってサイトが止まった