Open6

RDBメモ

zakkyzakky

インデックス概略

  • インデックスとはデータ構造
  • このデータ構造の値を用いて行での検索を行う
  • インデックスには1つ以上の列の値が入る (B+Treeインデックスでは実際のレコードの値のコピーを格納している)
  • インデックスはサーバー層ではなくストレージエンジン層で生成される
  • サーバー層に以下の恩恵をもたらす
    • サーバーがスキャンするデータ量の減少
    • サーバー上でのソート(インメモリソート)や一時テーブルの省略
    • ランダムI/OからシーケンシャルI/Oへの変更
  • インデックスは基本的にB+tree構造を取るため、ルートノードから各リーフノードまでの距離が等しくなる。このB+tree構造に既存のレコード群を写像することで、単純にレコードを縦に並べるよりも、スキャンする際に確認する必要があるレコードの数が少なくなる
    • インデックスと呼ばれるものも、結局のところ正体は列の値のうち一つ以上をノードに格納したデータ構造であり、B+treeの構造をした集合である。このインデックスという集合の中に目当てのデータが含まれてなければ、インデックスを活かした高速なスキャンはできないため、フルスキャンが必要になる
  • インデックスに含まれる列の値でのソート(Order By)も高速化できる。これは、インデックスというデータ集合は、ソートされた状態で値を格納しているため、Order By実行時にメモリ上でのソートを行う必要がないためである。逆に、インデックスに含まれない列の値をもとにソートのクエリを実行した場合、ディスクからレコード群を取得した後、メモリ上でそれらのレコードの値を展開した上でソートを行う(メモリソート)必要が出てくる
    • MongoDBでも同様
  • 複合インデックス (2つ以上の列の値を格納したインデックス)をスキャンに使用する際、インデックスを構成する列の順番が重要となる
    • 列が3つあった場合、1,2番目の列を無視してスキャン条件を組み立ててしまうとフルスキャンが必要になってしまう

参考記事

zakkyzakky

リレーションについて

  • データモデリングをするとは、現実の事象をモデルへとマッピングすること (写像する、とまで言えるか?)
    • 「マッピングする」とだけ言うと、どうやってマッピングするかの指針が示されない
    • まずは現実世界の事象を「ドメイン」という単位で切り出してみる
      • 唯一正解の切り取り方は存在しない
    • ドメインとは集合である
      • 「テーブル」の概念が頭にあると、つい集合をRDBにおける「テーブル」として考えてしまうが、ここでは集合とはより小さい粒度のドメインを指す
      • 例えば、年齢や給与額、データ型(Integr や Vartcher)
      • これらドメインは、テーブルでいうところのattribute
      • ドメインは集合なので、例えば年齢ドメインには 14, 33, 25, 77, 29 といった要素が入っている
        • D {x | x は Integer} という集合Dである
      • これら各ドメインの直積集合が、RDBでいうところの「テーブル」になる
        • なお、ドメインの直積集合の各要素(=元) のことを tuple (タプル) と呼ぶ
          • タプルは { 山田, 24, male } というようにドメインの直積集合の1要素であるため、各ドメインの要素を1つずつ持っている。いわば各ドメインの要素の「組」である
          • pythonにあるtuple型のtupleはこれ
    • ドメインの直積集合の部分集合のことを、リレーションと呼ぶ
      • ドメインの要素は1つの値のみを持つ、という定義を満たしたドメインのみで構成された直積集合の部分集合のことを、第一正規化されたリレーションであるとみなす
zakkyzakky

結合演算について

  • natural join (= inner join) は、結合する2つのリレーションR, S において、結合条件としてしていたそれぞれのattributeの値が結合条件を満たすそれぞれのtupleを、横方向に結合させたもの
    • RとSの直積集合 T を取った上で、結合条件を満たす T のtupleのみを抽出した部分集合を生成する、と捉えてもよい
    • 結合条件を満たさないtupleは抽出しないため、リレーションRまたはSが有していたtupleが失われた集合になることもある (相棒にnullを許容しない)
    • natural join は単に join とだけ呼ぶこともある
  • outer join は、結合条件の比較の際に、比較先のリレーション S の持つattributeの値が null であっても、比較元のリレーションのtuple は受け入れて結合tupleを生成する
    • left outer join, right outer join, full outer join の概念を考えることができる
zakkyzakky

楽観的ロックと悲観的ロック

トランザクションとは

ビジネスロジック上、Atomicに扱いたい一連の処理の一貫性を保証するための仕組み

楽観的ロックとは

トランザクションの最後に行うCommitの直前に、更新対象としているレコードがトランザクション開始時点から更新されていないことを確認するタイプのロック。
もし更新されているのを検知したら、トランザクションはAbortされる。これにより、更新の競合を防ぐ。
一つのレコードに対して、複数のトランザクションが並行して走ることになる。

悲観的ロックとは

トランザクションの開始時点で、更新対象のレコードに排他的ロック(exclusive-lock)を取得するタイプのロック。
つまり、一つのレコードに対して複数のトランザクションが同時に走ることを許容しないことで、更新の競合を防ぐ。
共有ロック(shared-lock)を対象のレコードに取得しようとした際に、排他的ロックがかかっているレコードは共有ロックの取得を拒むので、トランザクションが並行して走らないことを担保している。
これは言い換えると排他的ロックを取得する頻度が高いと、時間あたりトランザクション実行数が減少するということになるので、トランザクション実行効率が落ちると言える。

参考: https://enterprisecraftsmanship.com/posts/optimistic-locking-automatic-retry

zakkyzakky

コネクションプーリング

一度確立したコネクションは、利用後も破棄せずにアイドルコネクションとして保持することができる。
アイドル状態としてコネクションをプールしておくことで、コネクションを再利用するときに新規でコネクションを張る必要がなくなる