200 行で SQL を覚え、
3,200 万行でデータベースを理解する
第一部はある小さなオンラインショップのデータ(顧客 10 人、注文明細 200 行)で、SQL とデータベースの考え方を手を動かして身につける。 第二部は同じスキーマが 3,200 万行になった世界で、なぜ速いか・なぜ遅いかを実行計画で確かめる。
どのラボも書き換えて実行できる。壊しても npm run seed:basics /npm run seed で作り直せる。
まず 1 回、叩いてみる
見る場所: 結果の表。10 人の顧客
第一部 DB 基礎編
ある小さなオンラインショップの 200 行のデータで、SQL とデータベースの考え方を身につける。
- 00 · 15 分無料データベースとは何か表計算のシートと同じ見た目の表から出発し、文字列で頼むと表が返る相手としてデータベースを掴む。
- 01 · 25 分無料テーブルを読む、作る顧客一覧の表を開き、行・列・主キー・型・NULL に名前を付ける。自分でテーブルを作って行を入れる。
- 02 · 30 分無料欲しい行だけ取り出す「未発送の注文を新しい順に 10 件」。WHERE / ORDER BY / LIMIT と、NULL の三値論理という最初の驚き。
- 03 · 25 分無料数える、合計する、束ねる「今月の売上は?」「顧客ごとの注文数は?」。集計関数と GROUP BY、そして 0 件の顧客が消える問題。
- 04 · 35 分無料テーブルをつなぐ注文に顧客の名前を付ける。JOIN で行が増える・消える感覚、LEFT JOIN、中間テーブル、なぜテーブルを分けるのか。
- 05 · 25 分無料データを変えるINSERT / UPDATE / DELETE。WHERE を忘れると全行が変わる。影響行数を先に数える習慣と、制約が守ってくれること。
- 06 · 25 分無料クエリの中にクエリを書くSELECT の結果は値にも集合にも表にもなる。IN / EXISTS / 派生テーブル / WITH と、NOT IN の罠。
- 6+ · 20 分 · 補足無料補足: 行を潰さずに集計するウィンドウ関数。ROW_NUMBER、累積和、前の行との差。飛ばしても第二部に影響しない。
- 07 · 30 分無料途中を見せない注文を入れて在庫を減らす 2 つの文を、1 つの操作にする。2 セッションで「COMMIT 前は他人に見えない」を体験する。
- 08 · 25 分無料索引という実物SHOW INDEX で見える「名前と列を持つ物」。EXPLAIN で「全部読んだ」と「索引で引いた」を初めて見分ける。
- 09 · 30 分無料アプリから使う接続は相手側のスレッド。プレースホルダと SQL インジェクション、ORM が発行する SQL、N+1。第二部への橋。
第二部 パフォーマンス編
同じショップの 1 年後。注文明細が 3,200 万行になり、アラートが鳴っている。障害調査の手順として実行計画を読む。
- 00 · 15 分無料1 年後のショップと、この計器盤3,200 万行の注文明細。4 つの計器と 4 つのタブ、実行計画という実物、キャッシュの温度。
- 01 · 25 分無料フルスキャンとインデックス — 障害 1: マイページが 4 秒かかる同じ 64 行を返すのに 4 秒と 0.2ms。ページ・キー・木の順で、インデックスの中身を開ける。
- 02 · 25 分有料実行計画を読む木は下から上へ。cost / rows / actual time / loops。推定と実測のズレが、調査で最初に見る場所。
- 03 · 25 分有料選択性 — 障害 2: インデックスを足したらタイムアウトが増えた9 割が同じ値の列に索引を張ると、オプティマイザが間違える。選択性、統計情報、ヒストグラム。
- 04 · 30 分有料複合インデックスとカバリング — 障害 3: 購入履歴の月次集計が遅い「この顧客の直近 30 日」を最速で返す設計。複合インデックスの列順と、本体行を読まないカバリング。
- 05 · 25 分有料InnoDB の中身テーブル本体は主キーの B-tree。同じ行数でも連続とランダムで 25 倍違う理由、バッファプール、UUID 主キー。
- 06 · 25 分有料ソートとページング — 障害 4: 注文一覧の 1,000 ページ目が開かないSort が消える条件、ソートのディスク溢れ、OFFSET の線形コストとキーセットページング。
- 07 · 25 分有料JOIN — 障害 5: 商品別の売上画面が終わらないNested loop と Hash join。loops の意味、結合列の両側の索引、同じ索引・同じ答えで順番だけ違うと 1,000 倍。
- 08 · 25 分有料書き込みの代償と、稼働中の変更インデックスは書き込みで払う。INSTANT / INPLACE / LOCK=NONE で止めずに変える。
- 09 · 35 分有料ロック — 障害 6: セール開始直後に注文が失敗する在庫行のロック待ち、逆順ロックのデッドロック、ギャップロック、インデックスのない UPDATE が全行を止める。
- 10 · 30 分有料同時に来る同時接続と p99、集計バッチが API を遅くする理由、ホットロウ、スロークエリログ、本番でしか学べない残り。