アルパカログ

Webエンジニア兼マネージャーがプログラミングやマネジメント、読んだ本のまとめを中心に書いてます。

もっと早く読みたかったアンチパターン集「失敗から学ぶRDBの正しい歩き方」まとめ

RDBには様々なアンチパターンがある。

「失敗から学ぶRDBの正しい歩き方」は、初級者が陥りがちなRDBのアンチパターンを紹介し、それぞれの対処法を紹介している。

このエントリでは、「失敗から学ぶRDBの正しい歩き方」の内容をまとめる。

章ごとのまとめ

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になる構造は「マジックビーンズ」と呼ばれるアンチパターンであり、下記のような問題を引き起こす。

  • CRUD機能を公開することでビジネスロジックのModelとは別の場所から呼ばれることがある
  • Controller層やService層にビジネスロジックが記載された「ドメインモデル貧血症」をもたらす
  • データベースにアクセスする場所が散在し、ビジネスロジックがデータに依存するため、ユニットテストが難しくなる

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

以上、このエントリでは、「失敗から学ぶRDBの正しい歩き方」の内容をまとめた。

参考になった方は、ぜひ「はてブ」やSNSでシェアしていただければ幸いである。


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

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

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

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