「失敗から学ぶRDBの正しい歩き方」
この本を読むと得られるもの
以下の20個のキーワードに対して、具体的な失敗例から、アンチパターンに陥らないための方法について理解できる
- データベースの迷宮
- カラムの命名が適当、外部キー制約なし
- 失われた事実
- 過去の事実や過程が重要になるモデルに対して、履歴を残していないと思わぬバグに繋がる(消費税率の履歴)
- やりすぎたJOIN
- JOINの濫用はパフォーマンスに大きく影響を与える
- 効かないINDEX
- INDEXの仕組み(NDEXが効くケースと効かないケース)を理解せずに、闇雲にINDEXを貼っても逆効果
- フラグの闇
- つい安易に追加しがちな削除フラグやstatusカラムのデメリットについて
- ソートの依存
- クエリの実行順序、ソートのアルゴリズムを理解しないままORDER BYを使用すると、パフォーマンスに影響を与えることがある
- 隠された状態
- 意味を含んだIDなどはアンチパターンの代表
- データにビジネスロジックを含める、複数の意味を持たせるなど
- 意味を含んだIDなどはアンチパターンの代表
- JSONの甘い罠
- JSONデータ型のカラムは最終手段として使う
- 使っても問題ないケースがかなり局所的
- 強すぎる制約
- DBの制約は、強すぎると、ビジネスロジックや仕様がDBに混入してしまい、本来アプリケーションレイヤーでの改修だけで済むような内容でも、DBへ影響が波及してしまうことがあるので、制約は強弱を理解して使うことが重要
- 大抵のシステムは、弱い制約で十分
- 転んだ後のバックアップ
- バックアップは取れば終わりではなく、バックアップをとって、それをリストアするところまでがバックアップ。定期的にバックアップの検証を行うことが重要
- 見られないエラーログ
- システム運用の際に見やすいログが出力できるように、システム設計の段階からログ設計を行なっておくべき
- 監視されないデータベース
- RDBMSのモニタリングの重要性
- 知らないロック
- ロックの処理と、どのような時にロックが発生するのかを理解していないと、デッドロックを引き起こす危険性がある
- ロックの功罪
- 並列処理でデータの一貫性を保つためにはトランザクションとその分離レベルについて正しく理解する必要がある。そうでなければ、パフォーマンスに著しく影響を及ぼす可能性がある
- 簡単すぎる不整合
- パフォーマンスを優先してテーブルを正規化しないことにより、参照生合成などを担保できず、不整合なデータが発生してしまう
- キャッシュ中毒
- 速度を重視すぎて、キャッシュを多用すると見えてはいけないデータが見えてしまうなど思わぬ事故につながるので、メリットデメリットをよく理解して使う必要がある
- 複雑なクエリ
- ノーチェンジコンフィグ
- 塩漬けのバージョン
- フレームワーク依存症
以降で、全てを紹介し切ることはできないので、私自身が特に気づきを得た部分を抜粋して紹介(全内容を見たい方はぜひ購入して読んでみてください。)
第1章 データベースの迷宮
P2 RDBにおける制約
当たり前のことではあるが、Primary key制約は Check制約などRDBではどんな制約が利用できるのかを整理・理解しておく重要性
P10 本番適用済みのテーブルのカラム名の変更を安全に変更する方法
別のカラムを用意する→元のカラムと同じ内容が別のカラムにも保存されるようにする→別のカラムに値が入り切ったら新しいカラムの値を見るようにアプリケーション側の向き先を変える→元のカラムをドロップする
第3章 やりすぎたJOIN
P32 JOINアルゴリズムの種類
- 深く考えずに多段JOINを利用すると、パフォーマンスに大きな影響を与える危険性を含んでいる
- 現在行われているJOINが、 Nested Loop Joinなのか、Hash Joinなのか、Sort Merge Joinなのかを理解して使用しないと意図しない動作になるので注意が必要
- 内部表に適切にINDEXを貼る、フィルターである程度テーブルサイズを小さくしてからJOINする、などしてJOINによるパフォーマンスの低下を防止することが重要
第4章 効かないINDEX
P45 インデックスが効かないケース
- そもそもレコード件数が少ない
- 数万から数十万行以上のレコードがない場合、そもそもテーブル全体をスキャンした方が効率的な場合が多い
- クエリの検索結果が多い
- 検索結果がテーブル全体のレコードの20%以上の場合はテーブル全体をスキャンした方が効率的な場合が多い
- クエリの条件に指定するカラムを使っていない(インデックスを貼ったカラムを条件として指定できているつもり)
- ・・・where age * 10 > 100;
- ・・・where age > 100/10;
- カーディナリティの低いカラムに対する検索
- 性別のカラムのようなそのカラムで検索した場合の結果に重複が多いカラムにインデックスを貼っても効果が低い
- 曖昧な検索
- LIKE検索で、標準でインデックスが利用できるのは前方一致のみ
- 後方一致はreverse関数で反転させて別カラムに保存するか、PostgreSQLであれば式INDEXが使用できるので、reverse関数の結果に対してもインデックスが有効になる
- 部分一致でインデックスを使用したい場合は全文検索などを利用する必要がある
- 統計情報と実際のテーブルで乖離がある場合
- 定期的に作成される統計情報が、バッチ実行などで大規模な更新が行われると実際のデータとは乖離した統計情報が作成されることがある
- このような乖離が生まれるとインデックスがうまく効かないケースが発生する
第6章 ソートの依存
P75 ORDER BY区狙いの狙いのインデックス
- RDBのソートはコストが高いので、大きなデータをソートしたい場合はアプリケーション側で行う
- 仮にRDBでソートを実施する場合も
- where句でデータを絞り込んでからソートする(その際にインデックスを使用できるとなおよし)
- カーディナリティが少ない(あるカラムでフィルタリングした際に、結果のレコードの該当カラムに重複が多い)場合は、うまくインデックスを活用できないので、order by区狙いのインデックスを活用する
- データ量が増えると、クイックソート(メモリ上でのソート)ができなくなり、ある日突然外部ソート(ソート結果をファイルに吐き出す)になりパフォーマンスが急激に低下するといった現象も発生しうるため、注意が必要
第7章 隠された状態
P90 意味を含んだID
- データにロジックを持たせる等はアンチパターン(IDの前2桁は県番号、そこから3桁は部門ID・・・)
- 一見しただけでは、その意図を理解できない(アプリケーション側でロジックを持たせるべき)
P91 複数の目的に使用されるテーブル
- 似たような属性の場合、1つのテーブルに保存してしまいがちだが、分けた方がいいケースが圧倒的に多い
- データの属性によって入る値が変わるカラムやNULLが入るようなカラムがある場合は複数の目的で使用されるテーブルとなってしまっている可能性が高い
- 例えばユーザーテーブルを管理者テーブルと一般ユーザーテーブルに分ける
第8章 JSONの甘い罠
P109 JSONカラム型にはデメリットが多い
- 必須属性を指定できない
- データ型を強制できない
- 参照整合性制約を強制できない
P112 JSONカラム型のユースケース
- 保存したJSONをそのままJSON型としてアプリケーションでも扱うようなケース
- 保存したデータに対してUPDATEすることがない。またはほとんどない
ただし、必ず先に正規化できないか検討した上で、使用するべき
第13章 知らないロック
P172 ロックのレベルと粒度
- ロックのレベルと粒度について理解することがまず重要
- レベル
- 排他ロックと共有ロック
- 粒度
- 表ロック、行ロック
- レベル
第17章 複雑なクエリ
P233 SQLの構文評価順序
まとめ
いかがでしたでしょうか。本記事では「失敗から学ぶRDBの正しい歩き方」の要約と読んだ感想について紹介させていただきました。DB初心者〜中級者が陥りがちな20個のキーワードを元に、メリットデメリット、アンチパターンにはまらないようには具体的にどうすれば良いのかという方法論について学習できる一冊になりますので、ぜひ皆さんも書籍を購入して学習してみてください。