masakazu-takewakaのブログ

たまに書きます。

「理論から学ぶデータベース入門」を読んだ

読んだのでメモ。

www.amazon.co.jp

1章 リレーショナルモデルとSQL

リレーショナルモデルとは、リレーショナルモデルとSQLにおける演算の比較

  • リレーショナルモデルはデータモデルの1つで集合論がベース
    • 集合 -> 要素が重複しない、要素が集合に含まれるか確実に判断可能
      • つまり集合にNULLはない
  • リレーショナルモデルのリレーションはSQLでいうテーブル
  • 次のシンブルなSELECT文は3つのリレーションの演算 SELECT (射影) FROM (直積) WHERE (制限);
  • SQLにあってリレーショナルモデルにないもの

2章 述語論理とリレーショナルモデル

述語論理の簡単な導入、リレーショナルモデルの演算を述語論理で表現

  • リレーショナルモデルは述語論理(論理学)もベースにしている
  • 命題 -> ある物事についての文で、真偽値が問えるもの
  • 命題論理 -> 既知の命題から他の命題の真偽値を導く
  • 量化
    • 集団を対象にした真偽値
      • ある集団のすべての要素がある性質を満たすか
      • ある集団にある性質を満たす要素があるか
  • 命題論理 -> 量化で拡張 -> 述語論理
  • 閉世界説
    • 未知の事実は存在しない(全ての事実はリレーションに含まれる)という考え方
      • 全ての問いがリレーションの演算で解決する

3章 正規化理論(関数従属性)

関数従属性、1NF - BCNFの解説

  • 正規化理論はデータの不整合性の原因である「重複」を取り除く方法
  • 候補キー
    • タプルを一意に特定できる、無駄な要素が含まれない属性の集合(無駄が含まれるとスーパーキー)
    • 1つのリレーションにそのような集合は複数存在しうるので「候補」
  • 関数従属性
    • AがわかればBもわかるとき、BはAに関数従属する
  • 無損失分解
    • リレーションをjoinで再構築可能なように複数のリレーションに分解する
  • 1NF
    • リレーションであるかどうか
      • 行と列に順序がない
      • 行が重複しない
      • 値にNULLがない
      • 値がこれ以上分解できない(意味的に)
  • 2NF, 3NF
    • 無損失分解で関数従属性を取り除いていく
  • BCNF
    • 自明なものを除いて関数従属性が存在しない
    • 多くの場合ここまでくると5NFまで満たしている

4章 正規化理論(結合従属性)

結合従属性、4NF - 6NFの解説

  • 結合従属性 -> リレーションRの見出しの部分集合をjoinしてRが再構築できれば、Rは結合従属性を持つ
    • 無損失分解ができるということ
    • 関数従属性は結合従属性の一部
  • 4NF - 6NF
    • 無損失分解で結合従属性を取り除いていく
    • 5NF -> 暗黙的ではない全ての結合従属性が取り除かれた状態。最後の正規形
    • 6NF -> 全ての結合従属性が取り除かれた状態。無駄な結合が増える

5章 リレーションの直交性

  • 直交 -> 2つ以上のリレーションに同じタプルが含まれない
    • 複数リレーション間の重複を解消するもの
    • 異なるテーブルに意味は違うが値は同じレコードが存在することもあるので、直交化は必須ではない

6章 ドメインの設計戦略

  • ドメイン -> 属性が取り得る値の集合
  • 優れたDB設計がしたいならまず優れたアプリケーション設計スキルを身につけよう
    • アプリケーションへの理解が曖昧であったり、設計に欠陥があると、DB設計も悲惨になる
  • DBは本質的なデータを扱う
    • 表示上の問題をDBに持ち込まない
  • ナチュラルキーの設計をする際は、用いる値がIDとして機能するかきちんと吟味する
    • ISBNは長年運用されているが、書籍の旧版と新版に同一の値を割り振る出版社のせいで、実はIDとしては機能していない
  • パーツに意味があるIDはその値に依存した処理で問題が発生する
    • 冗長で非効率なクエリになりがち
      • 1NFでないから

7章 NULLとの戦い

  • NULL -> unknownな状態
    • 1NFを満たさない
    • 閉世界説を破綻させる -> リレーショナルモデルも破綻
  • オプティマイザへの弊害
    • オプティマイザはクエリの結果が等価であることを数学的に証明できる組み合わせから選択する
      • NULLがあると数学的に証明できる組み合わせが激減する
  • クエリのコスト見積もりに影響
    • インデックス上でNULLはそのインデックスの先頭か最後尾にまとめられる
      • IS NULLでその部分を全スキャンする必要が生じる

9章 履歴データとの戦い

  • 履歴データとRDBは相性が良くない(リレーショナルモデルと噛み合わない)
    • リレーションでないので1NFを満たさない
      • 時間によってクエリの実行結果が変わる
        • リレーションとはある時点における事実のなので、これはリレーションではない <- ??
      • 初期値NULLのカラムが登場しやすい
      • 暗黙の意味を持つ特定のレコードの存在
        • e.g.) ある商品の最新の価格
          • リレーションとは「ある述語に当てはめると真になる要素の集合」なのでこれはリレーションではない

11章 インデックスの設計戦略

B+ Tree

  • 文字列は前方一致である必要あり
    • 文字列の左端からソートされてるから
    • マルチカラムインデックスの場合、左から順にカラムを指定
  • インデックスの更新 -> 削除と挿入 -> 高コスト

パーティショニング

  • 刈り込みによって検索が速くなることがメリット
    • 刈り込みが効かないとそれぞれのパーティションに検索をかけることになりむしろ高コスト
  • パーティショニングが有効なケース(多くのケースはインデックスで十分)
    • キーのカーディナリティが低い(大量にフェッチされる)場合
      • カーディナリティの低いインデックスで大量のデータをフェッチするとインデックスページとデータを格納したページを行き来して高コスト
        • パーティショニングをすれば1つのパーティションをスキャンするだけ済ませることが可能
    • アクセスの局所性が存在する場合
      • 刈り込み & ローカルインデックス(インデックスは各パーティションごとに作成される)

最適なインデックスを探す

  • クエリが決まってから高速化のためにインデックスを設計する
    • 先にインデックスを決め打ちしてそれに囚われてはいけない

where

select * from t1 where c1 > 100 and c2 = 'abc';

インデックスは(c2, c1)の順で貼る。('abc', 100) < (c2, c1) < ('abd', c1の最小値) という範囲がインデックスから検索される。

(c1, c2)だとc1 > 100が開いているため、B+ ツリーだとc2が解決されない。<- ??

join

select * from t1 join t2 on t1.c1= t2.c2
where t1.c3 =100 and t2.c4 = 'abc';

joinでは内部表(結合される方のテーブル)にインデックスが効く。

t2.c2がユニークではない場合(オプティマイザが駆動表と内部表を入れ替えることがある)、(c2, c4)のマルチカラムインデックスがあるとクエリが速くなる可能性がある。

t2をフェッチする時点でt1.c1 = t2.c2t2.c4 = 'abc'の両方が適用されて、テーブルをフェッチした後にwhere句で制限する手間が省かれる。

14章 トランザクションの本質

トランザクションとは

データを正しく保つための手法。DBに限った概念ではない

トランザクションの機能

  • スケジュール
    • 同時アクセスによって生じるデータの不整合を防ぐ
  • クラッシュリカバリ
    • 異常終了したらロールバックする、また再起動によって必要なデータを再構築する

スケジューラー

  • 異常の種類
    • lost update
    • inconsistent read
    • dirty read
      • まだコミットされていないデータを読んでしまうこと
    • non repeatable read
      • 同じデータを複数回読んだ時、トランザクション内でそのデータには書き込みをしてないにも関わらず違う値になっていること
    • phantom read
  • 分離レベル
    • 基本的にはserializableを選ぶ(PostgreSQLのデフォルトはserializable)
    • 性能が欲しい場合はread-commitedかrepeatable-readを選択して必要な時だけ明示的にロックを取ることもできる
    • read-uncommitedはロールバックが機能しないので使われない

クラッシュリカバリ

  • トランザクション理論が定義するDBサーバーのコンポーネント
    • stable DB -> ストレージ上のDB
    • stable log -> ストレージ上の履歴
    • DB cache -> メモリ上のDB
    • log buffer -> メモリ上の履歴
  • 各種更新はDB cacheで行われ、フラッシュすることでstable DBに反映される
  • リカバリは2手順
    • stable logに格納されたログエントリをREDOしてDB cacheを再構築
    • 完了していなかったトランザクションのログエントリを全てUNDO