「失敗から学ぶRDBの正しい歩き方」を読んで[備忘録]

# はじめに
自分の備忘録と情報共有のために、私が読んだ技術書について各章のサマリーと感想をまとめました。
書評と言った類いの記事ではないですが、覗いていただければ幸いです。
(備忘録としても兼ねているので、個人的に刺さったものについて強調を入れています。)

今回は「失敗から学ぶRDBの正しい歩き方」について書かせていただきます。


<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/268028/2f447fcb-c344-93eb-bc25-88f6894b5ef5.png" width=30%>



# 本の内容
DB、SQLにおけるアンチパターンの解説した本。全20個のアンチパターンについて現場エピソードから対応・対策法、心構えが述べられている。各章簡潔にまとめられているため、リズムよく読み進められる。

技術書というよりはDBに向き合うための心構え、姿勢を学ぶための読み物に近い。
なので、オススメしたい層としてはその他の設計本に鼻を折られた初級エンジニアさん(自分然り)が当てはまるだろう。


# 読む目的
テーブル設計についての基本知識と実務での心構えを広く浅く学ぶ。

-> 採用面接でテーブル設計についての技術試験があったがうまく答える事ができず、最低限のことは頭に入れておく必要があると思ったから。
また、実務で設計について取り組んだ事がなかったが、おそらく次の職場で担当することもあるかと思い、先取りとしての読書。

# 各章要約
##1.データベースの迷宮
**その場しのぎの命名、カラム追加はするな。**後から見たときのそのカラムを名前から把握しやすいものをつけよ。

## 2.失われた事実
**履歴を保存せよ。後から遡ったときに、遍歴が辿れないと、トラブル時の状況把握で使える情報が減ってしまう。**
->払い戻しなどの取り消し処理に対応できるか、発送状況などのステータス変化を追えるか等のパターンについて確認する。

## 3.やりすぎたJOIN
**JOINは掛け算であり、SQLの中でもっとも負荷の高い操作。**テーブルが増えると指数関数的に処理負担も増加する。
対策として、JOINを使わずに欲しいレコードを取得できないか、テーブルを小さくしてJOINできないかを考える。

## 4.効かないインデックス
インデックスが効かなくなる5つの要因を抑えてアンチパターンを避ける。

## 5.フラグの闇
**「とりあえず削除フラグ」は、クエリの複雑化、UNIQUE制約が使えない、カーディナルが低くなる等の問題を含むのでやめる。**
対応策としては「事実のみを保存する」こと。テーブルに状態を保存するな。
-> 会員テーブルに削除フラグを持たせるのではなく、削除済み会員テーブルを作る等

## 6.ソートの依存
RDBはソートが苦手。なので、**ソート前にデータをちいちゃくする、INDEXを利用するのがよい。**
INDEXを利用したソートにはWHERE句狙いのソート、ORDER BY狙いのソートがある。

大きいデータをソートしたい場合の対応方法
1.アプリ側でのソートする
2.NOSQLを利用
3.キャッシュを利用する

##7.隠された状態
**テーブルにビジネスロジックを持たせるような設計はしてはいけない。**
汎用性を持たせることを目的としてアンチパータンに陥ることが多いが、これらは正規化によって解決できる。

考え方としては以下が有効
1.データに複数の意味を持たせない
2.1つのデータの責務を小さくする。
3.常に状態が見えるようにするために事実のみを保存する。

##8.JSONの甘い罠
JSON型にはRDBの特徴と引き換えに、スキーマから解放されるというメリットがある。
しかし、それによって以下の問題が生じる。
1.データの生合成が保てない
2.検索の複雑化
3.更新コストの増加

**スキーマレスに逃げることによって生じるデメリットは大きく、以下に該当すれば不採用とした方がが吉。**
1.正規化できそう(多くの場合、できる事が多い)
2.頻繁に更新を行いたいか

**基本的にJSON型は最後の切り札として考える。**

##9.強すぎる制約
**システムの仕様、ビジネスロジックに依存した強い制約をテーブルに用いるべきではない。**
これらの制約は仕様変更に弱く、思わぬ弊害を生む可能性がある。
**大切なのはバランスであり、テーブルの責務とアプリケーションの責務を正しく把握することが必要がある。**
妥協せずにDBと向き合うのだ。

##10.転んだ後のバックアップ
「バックアップは設定して終わりじゃない」。バックアップ関連の不具合はシミュレーションの怠りにより発生することが多い。

□ アンチパターン防止策。
1.定期的にバックアップーリストアを行う。-> バックアップ後は確認のため、ステージング環境で簡易リストアするのも有効。
2.バックアップ・リストア手順書を作る。(ユースケースも用意)
3.チーム内で定期的を訓練する。必ず複数人がバックアップできるようにし、属人化を防ぐ
ここらへんの心構えはDB初心者には嬉しい。

##11.見られないエラーログ
エラーログの運用における重要性を認識し、設計を怠らない。
-> ログはシステムからの警告。無視すれば痛い目に合う。
ログはその深刻度のよって、出力パターンを分けるべきであり、出力された時に緊急度を認知できるようにすべき。
同時に、**通知の仕組み、可視化の仕組みも、設計段階で考えれば運用を育てることにも繋がる。**

##12.監視されないデータベース
モニタリングはサービスの品質を可視化する。
エラーログと同様に、**重要性に対する認識の欠如によって発生する問題が多い。**
-> モニタリングの文化を育てることが大切

☆死活監視->チェック監視->メトリック監視と言った流れでステップアップさせていくのが定石

##13.知らないロック
DBごとにロックの振る舞いはことなる。**正しい知識を持つことで気づきにくいロックを防ぐことができる。**

例)
PostgreSQLではSELECTでもロックを取るので、このタイミングでLOCK TABLEが実行されるとデッドロックが発生する。
-> MySQLではLOCK TABLEが実行時にトランザクションをコミットするため上記の問題は発生しない。

##14.ロックの功罪
**トランザクションの分離レベルを理解せよ。**トランザクションの分離レベルは設定によってはトランザクション中の振る舞いに影響を与え、以下の関連現象を引き起こす。

□ 関連現象:
1.ダーティーリード-> 自分がコミットしていないデータが別Tに見れてしまう。
2.ファジーリード-> 別Tがコミットしたデータが参照できてしまう。
3.ファントムリード->別Tがコミットした追加、削除したデータが見れてしまう。
4.ロストアップデート->複数Tが並列にデータを更新した場合、後に実行したTの結果でデータが上書きされる。
(T->トランザクション)

**ロックの役割は並列度を保ったまま、データの整合性を維持することである。**
が、その多用は並列度を下げ、パフォーマンスに影響を与える。
=> 毒にも薬にもなるロックの功罪

##15.簡単すぎる不整合
**テーブルは基本的に正規化すべき。**非正規にせざる得ないように見えても、代替案をよく検討する必要がある。
(CHECK制約、ENUM型という代案もあるがいずれも変更にはALTERが必要。)
一度、非正規化が始まれば、変更することは容易ではなく、技術的負債を追うことになる。
**実装都合で非正規化するなどもっての外である。**

##16.キャッシュ中毒
キャッシュは速度面で問題なければ、基本的に使わないほうがいい。キャッシュを利用すべきかどうか、しっかりと検討すべし。
計算処理の重いデータに対してキャッシュを使えば、劇的なパフォーマンス向上につながるが、**キャッシュしたデータの状態を把握することが難しい**というデメリットもある。
-> 障害発生時、問題の切り分けが難しくなる。

参考:
キャッシュの悪夢 -> mixi/メルカリ

##17.複雑なクエリ
クエリが複雑化する要因は主に2つ

- **無知ゆえの剛腕** ->スキル不足ゆえに力技で解決されたクエリ
- **腐ったテーブルに腐ったクエリ** -> テーブルが複雑ゆえに、クエリも複雑になった

対策として、有識者によるレビュー、コーディング規約等の制約、大きなクエリの分割(責務の把握)、テーブル設計の最適化、等が上げられる。

##18.ノーチェンジコンフィグ
コンフィグの設定は属人化しやすく、放置されることが多い。**役割、意味を知り適切に管理することが大切。**

□ コンフィグで管理できること
1.DBのパフォーマンス
2.DNのセキュリティー
3.DBの振る舞い -> トランザクションの分離など

□ コンフィグの管理はどうすべきか
1.Infrastructure as Code -> インフラをコードで管理するという考え方。Ansibleなどのプロビジョニングツールも有効
2.Databse as a Service -> RDSとかフルマネージドサービスを使うことによって、高品質に設定されたDBを利用できる

##19.塩漬けバージョン
バージョンアップは文化。**目先の運用を優先してバージョンアップを怠るべきではない。**

□ バージョンアップをするための順序
1. バージョンアップの方法の決定
2. コンフィグの確認
3. リハーサル
4. バージョンアップ作業

##20.フレームワーク依存症
フレームワークは開発効率の向上という点で有益である一方、**テーブルをフレームワークに依存した設計にすることでアンチパターンの温床にもなり得る。**

□フレームワーク依存によるデメリット

- スロークエリの発行元を追うことが難しい。
- DB側の機能やデータの整合性が失われる。

以下、アンチパターンへの対策

- ORMが発行するSQLを意識する(RDB用に最適化されたものでないこともある)。
- DOAに基づき、Modelの中でテータを取り出すそうと取り出したデータを加工するそうを分ける。
-> いずれにせよ、メリットとデメリットのトレードオフを意識して設計をするのが大切。

参考)紹介されていたアンチパターン
-> こうゆうのがあるんだなで塩づけ

- マジックビーンズ
- STI
- ポリモーフィックス

## 感想
冒頭にも書いたが、知識以上に、DBに向き合うため際に姿勢について大変参考になった。
多くの場合、**アンチパターンは知識不足、その場しのぎの設計によって起きる。**知識不足の剛腕とはほんとよく言ったものだ。
これはアプリケーションにおいてもそうだと思うが、良い設計をするためには、仕様から目を逸らさずに正面から向き合い続ける必要がある。DBにおいても、実装優先の設計では自分たちの首を絞めることになり、何も知らずに任された後任者は尚のことしんどい思いをする。愚直に学び続けるは必須だろう。