Kenichi::Tech Blog

Web系エンジニアのブログ。

DB設計の進め方

重要な考え方

情報とデータ

データ

単なる事実の値。
これを永続化して蓄えるのがRDBの目的。

情報

データから生み出される、意味や目的のあるもの。
SQLで出力する。

更新(UPDATEとDELETE)は複雑さを生む

  • そもそも集合論的に更新はない。
  • 更新にはテーブルを見るだけではわからない複雑なルールを伴うため、極力更新を避けるようにDB設計すべき。
    • この価値観は、不足しているエンティティや属性の発見にもつながる。更新で対応していたことが、実はエンティティや属性が不足していたことが原因ということが発見されうるということ。
  • 更新がなければデータが失われることはない。
  • 更新を許す = 履歴や遷移の過程を残さなくてもいい ということ。そういう場合には更新をしてもよい。

典型的なアンチパターンを踏んでいないか確認する

システムの品質はDB設計で決まる

DBモデルの変更は、コードベースの修正に比べて非常に改修コストが高い。
これはデータのフォーマットがプログラムを決めているため(DAOの大原則)。
システムの品質はDB設計で決まる、という気概を持ちながら、適切にDB設計をする必要がある。

非正規化にはリスクが伴う

  • 非正規化は冗長化をすることに他ならず、更新不整合を起こす原因となる。
  • 検索のパフォーマンスは上がるが、更新のパフォーマンスは低下する。
  • データのリアルタイム性が失われる。

One Fact In One Place

一つの事実は一つと場所にのみ存在する。 これを実現するための指針が正規化。

設計の種類

論理設計

物理的な制約を無視した、理想的な設計。
まずはこれを行う。

以下4工程で構成される。

  1. エンティティ抽出
  2. エンティティ定義
  3. 正規化
  4. ER図作成

物理設計

パフォーマンスやハードウェアの性能(e.g. CPU, ストレージ etc...)など、現実問題への対処方法を考える。

以下5工程で構成される。

  • テーブル定義
  • インデックス定義: パフォーマンス向上のため
  • ハードウェアのサイジング
  • ストレージの冗長構成
  • ファイルの物理配置

論理設計

前提知識

エンティティの種類

  • リソース: 名詞で表されるエンティティ。イベント以外のエンティティと捉えたほうが楽かも。
  • イベント: 一般的には動詞で表されるエンティティで、属性に「~する」とつけて自然と成り立つもの。ただし、必ずしもこの方法で判別はできない。属性に1つの日時を持つ(ここで言う日時とは、DateやDateTime型のカラムすべてという意味ではない。アクティビティが行われる日時のことを指している。)。

関数従属

xを決めればyが決まる関係(y=f(x))。
ここで言うxが主キーにあたり、yはその他の属性にあたる。
主キーは単数 or 複数で成る。

部分関数従属

主キーの一部を決めれば値が決まる関係。
それゆえ、主キーが複数あるテーブルのみでありうる。

推移的関数従属

A→B, B→CゆえにA→Cという推移的従属性がある関係。 (A, B, Cは属性)

エンティティの抽出

要求文章内の5W1Hに下線を引く。
これらがエンティティや属性の候補になる。

ポイントは、端折らずにすべての要求を文字に起こし、この作業を行うこと。
ここでエンティティや属性の候補に抜けがあると、後続に誤りが伝搬するため注意する。

何はともあれ項目を全て列挙すれば良い。 この際に、実際の値も書き添えておくと、データのイメージが湧きやすくてよい。 楽々ERDレッスルp.11

エンティティ定義

  • 属性(カラム)の抽出: 主キーも含む。
  • 制約条件: NOT NULL制約, UNIQUE制約, CHECK制約(値の範囲)

正規化

データの冗長性を排除し、一貫性と効率性を保持させる。
第三正規化までで基本的に問題ない。

正規化とパフォーマンスはトレードオフであり、正規化を勧めると検索パフォーマンスは下がる。
パフォーマンスが下がるからといって、非正規化をすることはご法度。
非正規化をする = データを冗長化させる ことに異ならなく、それによってデータを整合性を持って保管する難易度が上がる。
非正規化はあくまで最終手段、他の手が全てだめだった時にだけ行う(基本行わないスタンスでいるとよさそう)。

注意点は、業務の視点からの正規化を行うということ。 なんでも重複するものをDRYにすることが誤りなように、正規化も思考停止にやるのは誤り。 あくまで業務的な意味合いで重複しているデータを排除する意味で正規化を行う。 一見同一項目と考えられる属性についても、値をよくみると一つにまとめてはいけないことがある。 このような正規化を行うと、結果としてプログラミング言語側に負担が強いられることになる。

第一正規化

1レコード1カラムに複数の値が入っているものを排除し、1レコードの1カラムに1つの値となるようにする。

第二正規化

部分関数従属を解消(=別テーブルに切り出す)し、関数従属のテーブルだけが存在するようにする。

第三正規化

推移的関数従属を解消(=別テーブルに切り出す)し、関数従属のテーブルだけが存在するようにする。

イベントとリソースを正しく設計

イベントエンティティ

イベントには、1つの日時属性しか持たないようにする。
日付が複数ある=複数のイベントを含む → 複数回更新が発生する(イベントの発生ごとにレコードに更新が走るため) → 複雑さを招くから。
逆に1つの日時属性しか持たないようにすれば、イベントエンティティの性質上、エンティティには1回のINSERTしか行われない。

リソースエンティティ

リソースに隠れたイベントを抽出する。
イベントがないかをよく確認する(アクティビティの日時を管理する属性が含まれないか etc...) or 今一度要求を文章化して、エンティティを考え直すところからやり直してみる。

バックアップ設計, リカバリ設計

達人に学ぶDB設計 徹底指南書 初級者で終わりたくないあなたへ: p.52~66

その他、DB設計のテクニック

https://ken1min86.hatenablog.com/entry/2023/06/23/182424

アンチパターン

https://ken1min86.hatenablog.com/entry/2023/06/23/182527

参考

DB設計のテクニック集

下書き。 URLがほしいので一旦公開。

サマリデータ

集計用のカラムを追加し、そこに保存する。 (達人に学ぶDB設計 徹底指南書 初級者で終わりたくないあなたへ p.149-152)

いつ使うのか

集計処理のパフォーマンスを向上させる必要がある時。
サマリデータを追加する = 推移的関数従属が生まれる ということなので、サマリデータは必要最小限にする。

選択条件の冗長化

同一のカラムを複数のテーブルに持たせる。 (達人に学ぶDB設計 徹底指南書 初級者で終わりたくないあなたへ p.153-156)

参考

  • 達人に学ぶDB設計 徹底指南書 初級者で終わりたくないあなたへ

がんばりすぎるあなたへ 完璧主義を健全な習慣に変える方法

リンク

https://www.amazon.co.jp/gp/product/B00GIHQOE0/ref=ppx_yo_dt_b_d_asin_title_o03?ie=UTF8&psc=1

目的

  • 普段疲れやすいが、その原因に自分の完璧主義が起因している気がしたため、それを確かめるため。
  • また、そうだった場合は対処法を知るため。

要点

重要な考え方

  • 全ての完璧主義が悪いわけではない。健全な完璧主義と不健全な完璧主義がある。この不健全な完璧主義を対処すれば良いだけ。
  • 時間は有限。本当に重要な20%のことに力を注ぎ、重要でない80%のことは流す。
  • 理想とするライフバランスを保つ
    • 私の場合は仕事5, 娯楽2
  • 全てのやりたいことややるべきことが終わることはない。それが現実。ゆえに、全てを終わらせてからと思ってもその時は訪れない。全てを終わらせようとすると、永遠に頑張り続けないといけない。だから、前向きに諦めて、休むときはしっかりと休む。休めない人の原因はこの思考にある。休まないと体を壊し、逆に非効率になる。
  • 量より質。
  • パレートの法則に従って、重要なわずかなこと(20%)にだけ力を注げば良い。逆に多数の重要でないことに対して力を注ぐのは勿体無い。そういうことはできるだけやらない。やる必要があるときは軽く片付ける。
  • 優先度 = 緊急度×重要度。そして、重要度=費用対効果が高いもの≒求められる完成度。重要度が低いものに対しては、必要以上に完成度を高めても効果は低いので、ざっと片付ければ良い。
    memo

私の不健全な完璧主義

  • 非現実的で達成できない目標を立てる
  • 優先度 = 緊急度×重要度であるが、重要度を見極めていないため、すべてのことに対して完璧を目指している
    • その結果、必要以上に力を使ってしまい疲弊する

私の不健全な完璧主義を治すための方法

  • 現実的な目標を立てる
    • 自分の能力を冷静に評価する。
    • 目標を具体化する。漠然な目標は達成することが難しい。
    • 目標を分割する。どのような具体的な行為によって目標を達成できるか、明確にイメージできる程度まで、目標を分割する。また、作業時間を見積もれる程度まで分割する。
    • スケジューリングする。作業時間を見積もり、どれくらいで達成できるかを適切に見積もる。ここを非現実的に短く見積もりすぎる傾向がある。それは、これ以前の工程を適切に行なっていないから。
  • 全ての物事に対して、優先度=緊急度×重要度(=費用対効果, 完成度)を見極める。
  • 特に、重要度を見極める。重要度とは費用対効果の度合いのこと。これが高いものに対しては、高い完成度を目指す。逆に低いものに対しては、完成度を高めても効果は薄いので、良い意味で手を抜く。重要度=必要な完成度と解釈し、その度合いにあった完成度を目指すと良い。
  • 自分の理想とするライフバランスを保つ。全てのやるべきこと、やりたいことが片付くことはない。だから、それらを片付けてから休むといった非現実的なことをしようとせず、前向きに諦めてゆっくりと休む。