アルパカログ

プログラミングとエンジニアリングマネジメントがメインです。時々エモいのも書きます。

【読書メモ】キミのINDEXは効いているか? / 失敗から学ぶRDBの正しい歩き方

失敗から学ぶRDBの正しい歩き方」はRDBMSを使う上でのアンチパターン集で、RDBMSを扱う初級から中級の全てのソフトウェアエンジニアに読んでもらいたい一冊だ。

本当に大事なエッセンスだけが詰まったこの本は、読みやすく一切無駄がないので、実際に手にとって読んで欲しいのだが、このエントリでは、私が読んで勉強になった箇所だけを思い切ってまとめてみたい。

章ごとのまとめ

1章 データベースの迷宮

データベースの寿命は、一般的にアプリケーションよりも長いため、技術的負債が積もると改修しづらくなる。早めのリファクタリングを心がけよう。

2章 失われた事実

履歴は更新ではなく追加し、最新レコードを有効レコードとしたり、削除処理も削除を表すレコードのINSERTとして保存したりして履歴(事実)が失われるのを防ぐ。

4章 効かないINDEX

usersテーブルにおいて、年齢カラムを使って30代のみを抽出するクエリがあり、30代のユーザは全体の10%なのでインデックスが効く。一方、20代のユーザは50%を占めている。10年後、急にインデックスが効かなくなるということが起こる。1

7章 隠された状態

「IDが9から始まるユーザは管理者」というように、意味を持たせたIDは「スマートカラム」と呼ばれるアンチパターンである。データには本来の意味以外の情報を持たせてはならない。2

10章 転んだ後のバックアップ

バックアップは正しく戻せるところまでがバックアップ。「バックアップを取っていたと思ったら中身はエラーログだった」なんてことにならないように、普段からシステム障害を想定した訓練をしておこう。

13章 知らないロック

RDBMSごとに異なるロックの振る舞いを正しく理解しておこう。

14章 ロックの功罪

トランザクション分離レベルと、RDBMSごとにデフォルトの設定が異なることを理解しておこう。3

18章 ノーチェンジ・コンフィグ

Amazon RDSなどのフルマネージドサービスを利用することで「自分たちでコンフィグを管理しない」という選択肢があることも頭に入れておこう。

カラム名typoリファクタリング例(1章)

delete_flagdelete_falg という名前で作られてしまった。

  1. 正しい名前のカラム delete_flag を追加する
  2. INSERTやUPDATEに対してトリガーを定義し、typoしたカラムと正しいカラムを同じデータにする
  3. アプリケーション側で新しいカラムを参照するように変更する
  4. 動作に問題がなければトリガーとtypoしたカラムをDROPする

失われる事実に設計段階で気付くために(2章)

次のようなことに気をつける。

  • 払い戻しなどの取り消し処理に対応できるか
  • 配送状況などステータス変化を追えるか
  • トラブル対応時、欲しい情報が失われていないか

RDBに履歴を持たせない場合、次のような方法もある。

  • 遅延レプリケーションを使う4
  • アプリケーションログとしてElasticsearchなどの分析ツールに保存する

よくあるテーブル設計のアンチパターン(7章)

EAV(Entity Attribute Value)
  • id: 1, 属性名: 年齢, 値: 30
  • id: 2, 属性名: 職業, 値: CRE

といったように、複数の目的に使われるカラムを持つ設計は「EAV」と呼ばれるアンチパターンである。必須属性が指定できない、データ型が指定できないなど、様々な弊害を引き起こす。

Polymorphic Associations
  • id: 1, 名前: おとよ, 住所: 東京都…, 参照先: User
  • id: 2, 名前: ミクシィ, 住所: 東京都渋谷区…, 参照先: Company

といったように、レコードによって参照する親テーブルが変わる設計は「Polymorphic Associations」と呼ばれるアンチパターンにあたる。外部キー制約が使えない、データを取り出すまでJOINする対象がわからないといった問題がある。

フレームワーク依存のアンチパターン(20章)

RDBMSのテーブルとアプリケーションのクラスが1:1になる構造は「マジックビーン」と呼ばれるアンチパターンであり、下記のような問題を引き起こす。

PHP製のフレームワークSymfonyでは、Modelを、データのCRUDだけを担うリポジトリクラス、サービスが必要なデータを取り出し加工するデータクラス、ビジネスロジックを担うサービスクラスの3層に分けうまく抽象化している。


感想など

実は少し前に、著者のそーだいさんが会社に来て講演をしてくださる機会があった。勉強会はほぼ本の内容に沿っていたし、懇親会にも出席しなかったので、情報量が大きく増えることはなかったが、嬉しかったのは、そーだいさんは今でも毎月欠かさずmixi日記を書いているという話を聞けたことだ。

そーだいさんが「この本は過去の失敗談を元にしている」と言っていたとおり、読んでいると「このアンチパターン踏んだことある/踏んじゃってるな…」というのがしばしばあり、読み進めるにつれ😇😇😇な心持ちになってくる。特に Polymorphic Associations は現在進行形でやらかしてしまっているので、次に設計するときはそうならないようにしなければと反省した。この本はそんな風に、実務の改善に速効性のある良著なのだ。


  1. インデックスを利用するためには、(1)検索結果がテーブル全体の20%未満、(2)検索対象のテーブルが十分大きい、という条件が必要

  2. 例えば「新たに○○なユーザを追加して」と言われたとき困る

  3. PostgreSQLのデフォルトはRead Committedで、MySQLのデフォルトはRepeatable Read

  4. 気付くのが遅れるなどして遅延時間を超えると事実が失われてしまうので注意