Open33

データ指向アプリケーションデザイン メモ

koichikoichi

1章 信頼性、スケーラビリティ、メンテナンス性に優れたアプリケーション

koichikoichi
  • 負荷のパラメータを定義する
  • システムの負荷を測る指標を明確にする。
  • パーセンタイルを計測する
  • レイテンシの分布を分析し、遅いケースの特定(キューイング遅延が原因の場合が多い)。
  • テイルレイテンシ
  • 最も遅いリクエスト(テイル)のレイテンシに注目。
  • 負荷改善のアプローチ
    • 垂直スケーリング: 単一サーバーの性能を向上。
    • 水平スケーリング: サーバーの台数を増加。
    • シェアードナッシング
    • エラスティック: 需要に応じてリソースを動的に調整。
koichikoichi

2章 データモデルとクエリ言語

koichikoichi

RDBとドキュメントDB、メリットデメリットについて語られている。
webサービスにおいてはだいたいRDBで事足りるイメージ。 (読んだあとに思いましたがそんなことなかったですね)ドキュメントDBは扱ったことないなぁ。

koichikoichi

宣言的コードは命令型のコードと比較して、実装の詳細を気にしなくて良くなる。結果のパターンだけ指定すればよくて、オプティマイザに委ねるから並列に実行しやすい。JSの例も納得。

koichikoichi

ドキュメントDBでの集計の例を見ていると、やっぱりRDBの仕組みの方がシンプルでいいなと感じてしまう。

koichikoichi

再帰CTEの例、難しいな。もう一回あとで読む。
実際に実装しながらじっくり理解したいな〜。

koichikoichi

chatGPT 先生によるグラフ型データモデルについての解説

グラフ型データモデルについて

グラフ型データモデルは、データ間の関係性を表現するために「頂点(ノード)」と「辺(エッジ)」を用いるデータ構造です。これは、従来のリレーショナルデータモデルと異なり、データそのものよりも、データ間の**繋がり(関係)**に焦点を当てています。

グラフ型データモデルは、特に複雑な関係性が重要な場面で使われ、ソーシャルネットワーク、ナレッジグラフ、レコメンデーションエンジン、交通ネットワークなどで強力なツールとなります。

基本構造

  1. 頂点(Vertices / Nodes)

    • データの個別エンティティを表します。例えば、人、都市、製品などの個別のオブジェクトです。
    • 各頂点にはプロパティ(属性)があり、名前、年齢、都市名、価格などの情報が含まれます。
  2. 辺(Edges)

    • 頂点同士の関係を表します。これにより、2つの頂点間に意味のある繋がりを表現します。
    • 辺もプロパティを持つことができ、関係の強度、関係の種類(友達、フォロワー、親子関係など)などの情報を持たせることが可能です。
  3. 有向グラフと無向グラフ

    • 有向グラフ(Directed Graph):辺に向きがあり、一方向の関係を示します。例えば、フォローやリンクなどです。
    • 無向グラフ(Undirected Graph):辺に向きがなく、双方向の関係を示します。例えば、友情やパートナーシップなどが該当します。

グラフ型データモデルの利点

  • 複雑な関係性の表現: ノード同士の繋がりが強調され、ネットワークやツリー構造などの複雑な関係を自然に表現できます。
  • 効率的なグラフ探索: 特定のノードから他のノードへどのように繋がっているか(例: 友達の友達は誰か?)といったクエリが効率的に処理できます。
  • 可変のスキーマ: 各ノードやエッジに異なるプロパティを持たせることができ、スキーマを柔軟に拡張できます。

主なデータベース

グラフ型データモデルを採用するデータベースは、「グラフデータベース」として知られています。以下は代表的なグラフデータベースです:

  1. Neo4j

    • 最も有名なグラフデータベースで、クエリ言語として Cypher を使用します。
    • ノードとエッジの関係を自然に表現でき、複雑なグラフ探索クエリを効率的に実行できます。
  2. Amazon Neptune

    • クラウドベースのグラフデータベースサービスで、RDF (Resource Description Framework) と Property Graph の両方をサポートしています。
  3. JanusGraph

    • 分散グラフデータベースで、大規模なグラフ処理をサポートし、複数のバックエンドストレージ(Cassandra, HBaseなど)と連携できます。
  4. OrientDB

    • グラフとドキュメントデータモデルの両方をサポートするマルチモデルデータベース。

グラフクエリ言語

グラフデータベースでは、関係性を探索するための専用クエリ言語が使われます。以下が主要なグラフクエリ言語です:

  1. Cypher (Neo4j)
    Cypherは、Neo4jで使われるグラフクエリ言語で、人間に読みやすく、ノードやエッジのパターンをシンプルに表現できます。

    例: ある人物が誰をフォローしているかを検索するクエリ

    MATCH (person:Person)-[:FOLLOWS]->(friend)
    WHERE person.name = 'Alice'
    RETURN friend.name;
    
  2. Gremlin
    Apache TinkerPopが提供するグラフクエリ言語です。分散グラフデータベース(JanusGraphなど)でよく使われます。

    例: Gremlinでのクエリ

    g.V().has('name', 'Alice').out('follows').values('name')
    
  3. SPARQL (RDFグラフに使用)
    SPARQLはRDF(リソース記述フレームワーク)形式のグラフデータを操作するためのクエリ言語です。RDFではデータが「主語-述語-目的語」の形式で表現されます。

    例: ある人物が誰をフォローしているかを検索するSPARQLクエリ

    SELECT ?friendName
    WHERE {
        ?person <http://example.org/follows> ?friend .
        ?person <http://example.org/name> "Alice" .
        ?friend <http://example.org/name> ?friendName .
    }
    

グラフ型データモデルの使用例

  • ソーシャルネットワーク: ユーザー(頂点)同士がフォロー、友達関係(辺)で結ばれる構造を管理するのに適しています。
  • 推薦システム: 顧客や商品の関係を分析して、関連する商品を推薦するためのデータモデル。
  • 知識グラフ: 例えば、Googleのナレッジグラフのように、概念(頂点)とその関連情報(辺)を管理し、検索クエリに対するより深い理解を提供します。
  • トラフィックネットワーク: 道路網(交差点が頂点、道路が辺)や電力網のモデル化にも使われます。

グラフ型データモデルの種類

  1. プロパティグラフ(Property Graph)

    • 頂点と辺の両方にプロパティ(キーと値のペア)を持たせられるのが特徴です。これは、Neo4jのようなグラフデータベースで採用されています。

    プロパティグラフの特徴:

    • 頂点と辺は識別子を持つ。
    • 頂点も辺も、プロパティ(属性)を持つことができる。
    • エッジはラベル付きで、関係の種類を表すことができる。
  2. RDF(Resource Description Framework)

    • RDFは、ウェブ上のリソース間の関係を表現するためのフレームワークで、**トリプル(主語-述語-目的語)**の形式でデータを表します。
    • 各トリプルは、あるリソース(主語)が、他のリソース(目的語)に対してどういう関係を持つか(述語)を表現します。

    RDFの特徴:

    • 主語-述語-目的語の3つの要素によるデータモデル。
    • 主にセマンティックウェブやオントロジーに用いられる。
    • クエリにはSPARQLが使用されます。

まとめ

グラフ型データモデルは、データ間の関係性が複雑な場合に強力なモデルで、データの接続性や関係性を効率よく扱えます。従来のリレーショナルデータベースに比べて、階層的なデータやネットワーク構造を直感的に扱えるため、特にソーシャルネットワークや推奨システムなどの分野で大いに活用されています。

koichikoichi

3章 ストレージと抽出

koichikoichi

最近、パフォーマンスが大幅に劣化するSQLの修正をしてしまったこともあり、それからデータベースの内部動作を知っておきたいなと感じている。ここはしっかり読んでおきたい。(まえがきを読んでの感想)

koichikoichi

ハッシュインデックス MEMO

キーに対して、データファイル内のバイトオフセット(その値がある場所)をマッピングする。
更新はなく、シーケンシャルに連続して書き込む(追記)。コンパクション処理&セグメントのマージ処理でセグメントが小さくなる。フラグメンテーションを起こさない。

以下、chatGPT先生によるまとめ

ハッシュインデックスの特徴と有効なケース

特徴

  • ハッシュ関数: ハッシュインデックスでは、データベースのインデックスキーに対してハッシュ関数を適用し、キーをハッシュ値(固定長の値)に変換します。これにより、特定のバケットにデータが格納されるため、インデックスの検索時間は理論的にはO(1)(一定時間)となります。
  • バケット: 同じハッシュ値を持つデータは同じバケットに格納されます。異なるキーが同じハッシュ値を持つことを「ハッシュ衝突」と呼び、この場合には追加の処理が必要ですが、基本的には高速な検索が可能です。

有効なケース

ハッシュインデックスが特に有効な場合は、以下のような条件があるときです。

  1. 正確な等価検索(=)に最適:

    • ハッシュインデックスは、特定のキーに対して等価条件=)での検索を行う場合に最も有効です。例えば、データベースで「IDが100であるレコードを取得する」といった単純なキー検索に対して非常に高速なパフォーマンスを発揮します。
    • 例: SELECT * FROM users WHERE id = 100;
  2. 固定長キー:

    • 固定長のキーや、ハッシュ化が容易なデータに対して効果的です。例えば、数値や短い文字列のフィールドに適用すると効率が良いです。
  3. データの分布が均等である場合:

    • ハッシュインデックスは、キーが均等に分布している場合に最も効果を発揮します。均等に分散されていると、ハッシュバケット内にデータが均等に割り当てられ、ハッシュ衝突の発生が少なくなります。
  4. 特定のデータに対するピンポイント検索:

    • 大量のデータの中から、特定の値を一発で見つける必要がある場合には適しています。例えば、ユーザーIDや商品コードなど、個別に識別可能な値で高速検索を行いたい場合です。

ハッシュインデックスが適さない場合

ハッシュインデックスは等価検索には適していますが、範囲検索部分一致検索には向いていません。具体的には以下のような場合には効果的ではありません。

  1. 範囲検索(<, >, BETWEEN)には不向き:

    • ハッシュ関数はキーをハッシュ値に変換するため、元のデータの順序情報を保持しません。そのため、範囲検索を行う場合(例: age > 30)や順序が重要なクエリでは、ハッシュインデックスは機能しません。
    • 例: SELECT * FROM users WHERE age > 30; では、ハッシュインデックスは使えません。
  2. 部分一致やLIKE検索:

    • ハッシュインデックスは文字列の部分一致検索LIKE)には効果がありません。なぜなら、ハッシュ値はデータの全体を一意に変換するため、部分的な一致や先頭文字列などに対応できないからです。
    • 例: SELECT * FROM users WHERE name LIKE 'A%'; では、通常のインデックスの方が有効です。
  3. ソートや順序に依存するクエリ:

    • データベースで検索結果をソートするクエリ(ORDER BY)にもハッシュインデックスは役立ちません。ハッシュインデックスはキーをバケットに割り当てるだけであり、順序の情報を保持しないためです。
    • 例: SELECT * FROM users ORDER BY created_at;
  4. 多くのハッシュ衝突が発生する場合:

    • キーの分布が偏っている場合や、ハッシュ関数が適切でない場合、ハッシュ衝突が多発します。これにより、インデックスが効率的に機能せず、逆にパフォーマンスが低下することがあります。

ハッシュインデックスとB-treeインデックスの比較

一般的にデータベースで使われるもう一つのインデックスがB-treeインデックスです。これと比較して、ハッシュインデックスの特徴を整理します。

項目 ハッシュインデックス B-treeインデックス
等価検索 非常に高速 高速
範囲検索 不向き 効果的
部分一致検索 不向き 効果的(特に前方一致)
順序の保持 順序情報を保持しない 順序情報を保持
衝突処理 ハッシュ衝突の可能性 衝突は発生しない
汎用性 特定用途に有効 幅広い用途に有効

まとめ

ハッシュインデックスは、シンプルなアルゴリズムで等価検索を高速に実行するために非常に有効ですが、汎用的にどのようなクエリにも適しているわけではありません。特に、範囲検索や部分一致検索のような、順序や部分的な一致が関わるクエリには効果がありません。

汎用性が高く、幅広いクエリに対応できるのはB-treeインデックスですが、単純なキー検索で最大のパフォーマンスを得たい場合にはハッシュインデックスが適しています。状況に応じて、どちらのインデックスを使うかを選ぶことが重要です。

koichikoichi

SSTable と LSM ツリー

1. SSTable (Sorted String Table)

  • セグメントファイルをキーでソートしたフォーマットを SSTable (Sorted String Table) と呼ぶ。
    • ソートされているので、すべてのキーをメモリに保持しなくてよい。
      • インデックスを疎にできるので、より高速にスキャンできる。
    • 書き込みは MemTable に追加される(例: Red-Blackツリーなど、インメモリのバランスドツリーのデータ構造)。
      • MemTableのサイズが大きくなると、SSTableに書き込む。この構造は、LSMツリー(Log-Structured Merge Tree)と呼ばれる。

2. LSMツリー

  • ソート済みのファイルのマージとコンパクションという原理を基盤とするストレージエンジンは、LSMストレージエンジンと呼ばれる。
  • LSMツリーは、複数のSSTableを管理し、必要に応じてマージ(コンパクション)を行うことで効率的な書き込みと読み取りを実現する。

3. ディスクアクセスとBloom Filter

  • DB上に存在しないデータを取得しようとすると、ディスクアクセスが発生する。
    • データが存在しない場合、ディスクアクセスが発生するが、アクセス最適化のためにBloom Filterを使用することで、ディスクアクセスを最小限に抑えることができる。
    • Bloom Filterは、データが存在するかどうかを確認するための確率的データ構造で、データが存在する可能性があるかどうかを返す。
      • 存在しないキーのために不要なディスクアクセスを回避するために使用される。
koichikoichi

Bツリー

  • 標準的なインデックスの実装で、多くのデータベースで使われている。

  • キーと値のペアをソートされた状態で保持するのは、SSTableと同じ。

    • データベースを固定サイズのブロックあるいはページに分割する。
    • ページには複数のキーと子のページへの参照が含まれていて、参照をたどることでキーを探す。
  • Bツリーにおける1ページ内の小ページへの参照数は、分岐係数と呼ばれる。通常は数百程度。

    • 分岐係数が大きいほど、木の高さが浅くなる。
    • 分岐係数が小さいと、木の高さが深くなる。
  • Bツリーに新しいキーを追加する場合、まずルートページからキーを探し、そのページにたどり着いたらキーを挿入する。

    • ページがいっぱいになったら、ページを分割して中央のキーを親ページに挿入する。
    • 親ページがいっぱいになったら、親ページを分割して中央のキーをさらに上位の親ページに挿入する。
    • これを繰り返すことで、木の高さが増えていく。
  • n個のキーを持つBツリーの高さは、O(log_b n)である。

    • Bツリーの高さは、分岐係数がbの場合、O(log_b n) である。したがって、分岐係数が大きいほど、木の高さは浅くなる。
koichikoichi

BツリーとLSMツリーの比較

  • LSMツリーは、書き込みを高速に行うことができるが、読み込みは遅い。

    • 書き込みが速い理由:

      • データはメモリ上の構造(Memtable)に蓄積され、ディスク上のデータ(SSTable)に対して直接上書きを行わない。そのため、複数ページのランダム書き込みが発生せず、シーケンシャルな書き込みができるため高速。
      • 定期的にSSTableをマージし、データを再配置(コンパクション)することで、古いデータを削除し、ディスク上のデータを整理する。これにより、データは圧縮され、ストレージのオーバーヘッドが低くなる。
    • 読み込みが遅い理由:

      • データが複数のレベルに分かれたSSTableに保存されるため、クエリ時には複数のSSTableをチェックする必要がある。
      • 読み込み中にコンパクションが発生すると、一時的にディスクアクセスがブロックされ、読み込みリクエストが待たされることがある。
      • これを最適化するために、Bloomフィルタなどの技術が使用されるが、それでもBツリーほどの読み込み性能は出ない場合が多い。
  • Bツリーは、読み込みを高速に行うことができるが、書き込みは遅い。

    • 読み込みが速い理由:

      • Bツリーでは、それぞれのキーが1つのページ内に存在し、インデックスはページ単位でツリー構造を形成している。キーは特定のページ内に1回しか現れないため、ディスクへのアクセスはツリーの深さに比例して少なく、検索が効率的に行える。
    • 書き込みが遅い理由:

      • Bツリーでは、データを追加するたびに既存のページに対して更新(上書き)が行われ、場合によってはページの分割が必要になる。このページ分割はランダムなディスク書き込みを伴うため、特に大規模なデータベースでは書き込み性能が低下する。
      • ディスク上の構造を頻繁に変更するため、書き込みコストが高くなりがち。

ストレージエンジンを評価する際に考慮すべきポイント

  • 書き込み頻度: LSMツリーは書き込みが多いワークロードに適しており、大量のデータの挿入や更新が頻繁に発生する場合に効果的。一方、Bツリーはランダム書き込みが遅いため、書き込みが頻繁に行われる場合は性能が低下する。

  • 読み込み頻度: Bツリーは、読み込み中心のワークロードに向いており、特にランダムなクエリのパフォーマンスが高い。LSMツリーは読み込みが多いとパフォーマンスが低下することがあるが、Bloomフィルタやキャッシュの活用で一部の問題は緩和できる。

  • ストレージ効率: LSMツリーはコンパクションを通じてデータを効率的に圧縮し、不要なデータを削除するため、ストレージの無駄が少ない。Bツリーでは、データが分散して保存されるため、ストレージの断片化が生じやすい。

  • レイテンシ: 書き込みや読み込みのパフォーマンスに関わる遅延は、特にリアルタイムアプリケーションやオンライン処理システムにおいて重要。LSMツリーでは、コンパクションが発生するタイミングでレイテンシが増加することがあるため、ワークロードに合わせて注意が必要。


まとめ

  • LSMツリーは、書き込みが多い環境に向いており、大量データの一括処理やキャッシュに依存したアプリケーションに適している。
  • Bツリーは、読み込みが多くランダムアクセスが重要なシステムで優れたパフォーマンスを発揮するが、書き込みが多い場合は性能が低下する。
koichikoichi

インデックスは、データを高速に検索するためのデータ構造ですが、そのインデックスからヒープファイル(実際のデータが保存されている場所)に至るまでに何回もディスクアクセスが発生することを「ホップ」と呼ぶことがある

koichikoichi

その他のインデックス、データの保持について

  • 連結インデックス: 複数の列を組み合わせて1つのインデックスを作成するインデックス方式。複数の列に対して効率的にクエリを実行できる。

  • 多次元インデックス: 複数の列にまたがるクエリを処理するためのインデックス手法。例えば、地理情報や座標系データのように、2次元またはそれ以上のデータを扱う場合に使用される。代表的なものにRツリーやKDツリーがある。

  • インメモリデータベース: すべてのデータをメモリ上に保持するデータベースで、高速なデータアクセスが可能。メモリ内にあるデータをディスクに保存することで、永続化やバックアップを行う。代表的なシステムにRedisやMemcachedがある。

koichikoichi

データベースの利用パターン

データベースの利用パターンには、異なるアクセスパターンを持つ2つの主な種類があります。

1. オンライントランザクション処理 (OLTP)

  • OLTPは、日常のビジネス取引を処理するためのシステムです。
  • リアルタイムでのデータの挿入、更新、削除が行われ、迅速な応答が求められます。
  • 主にトランザクション処理が中心であり、高い同時実行性を持つことが特徴です。
  • OLTPシステムは、通常、短期間のトランザクションが多数発生するため、応答時間を最小限に抑える必要があります。

2. オンライン分析処理 (OLAP)

  • OLAPは、データの分析や報告を行うためのシステムです。
  • 大量のデータを集計・分析し、ビジネスインサイトを得るために使用されます。
  • 複雑なクエリや集計処理が行われるため、パフォーマンスの最適化が重要です。
    • 分析のためのスキーマ (スタースキーマ):
      • スタースキーマは、ファクトテーブルとディメンションテーブルから構成されます。ファクトテーブルの各行はイベントや取引を示し、外部キーとしてディメンションテーブルのキーを持ちます。ディメンションは、イベントの各要素や属性を表します。

データウェアハウス

  • 分析用のデータベースは、一般に「データウェアハウス」と呼ばれます。
  • データウェアハウスは、OLTPの処理に影響を及ぼすことなく、分析処理を行うために設計されたデータベースです。
  • OLAP用にデータを整理・統合し、効率的な分析が可能になるように構築されています。
  • データウェアハウスは、大量のデータを長期間にわたって保持し、歴史的なデータ分析を行うために最適化されています。
koichikoichi

列指向ストレージにおける列の圧縮の例に出てきたビットマップエンコーディングについて by chatGPT

ビットマップエンコーディング

ビットマップエンコーディングは、データの圧縮や効率的なストレージを実現するための技法であり、特に列指向ストレージにおいて効果を発揮します。この手法は、特定の値が存在するかどうかをビットで表現することで、データの管理と検索を効率化します。

概要

  • ビットマップ: 各ビットは特定の値の存在を示します。例えば、ある列における異なる値の出現を、ビット列で表現します。各ビットが「1」であればその値が存在し、「0」であれば存在しないことを意味します。

  • : 値が「A」「B」「C」の3種類のデータがある列に対して、以下のようにビットマップを作成することができます。

    • 行がデータのインデックスを表し、列が値を示します。
    インデックス A B C
    0 1 0 1
    1 0 1 0
    2 1 1 0

    これは、以下のデータを表しています。

    • 行0: 値「A」と「C」が存在
    • 行1: 値「B」が存在
    • 行2: 値「A」と「B」が存在

利点

  1. ストレージ効率: ビットマップは、特定の値が存在するかどうかを示すため、特にユニークな値が少ない場合にストレージの効率が良い。
  2. クエリパフォーマンス: ビット演算を使用して、複雑な条件での検索が高速に行えるため、クエリの応答時間が短縮されます。
  3. 集計の容易さ: ビットマップを用いることで、特定の条件に一致するデータのカウントや集計が迅速に行えます。

欠点

  1. 高いメモリ使用量: ユニークな値が多い場合、ビットマップが非常に大きくなる可能性があります。これにより、メモリ消費が増加し、効率が悪化することがあります。(疎という)
  2. 書き込み性能の低下: データの更新や挿入が頻繁に行われる場合、ビットマップの再構築が必要になり、これが性能のボトルネックとなることがあります。

まとめ

ビットマップエンコーディングは、特に列指向ストレージシステムにおいて、データの圧縮と検索の効率を高めるための有用な手法です。ただし、データの特性によっては、ストレージの効率や書き込み性能に影響を与える可能性があるため、利用する際には注意が必要です。

koichikoichi

L1キャッシュとは

概要

L1キャッシュは、CPU内部に設置された最も高速なメモリキャッシュであり、プロセッサがデータに迅速にアクセスできるように設計されています。L1キャッシュは、CPUコアごとに存在し、主に2つの部分に分かれています。

特徴

  1. 速度:

    • L1キャッシュは、CPUの内部で最も高速なメモリであり、主記憶(RAM)に比べて遥かに高速です。これにより、データの取得時間が大幅に短縮されます。
  2. 容量:

    • L1キャッシュの容量は通常非常に小さく、一般的には32KBから128KB程度です。サイズが小さいため、アクセス速度が非常に速くなります。
  3. 構造:

    • L1キャッシュは、通常、データキャッシュと命令キャッシュに分かれています。
      • データキャッシュ: CPUが実行する計算に必要なデータを保持します。
      • 命令キャッシュ: CPUが実行するプログラムの命令を保持します。
  4. 階層構造:

    • L1キャッシュは、CPUのキャッシュ階層の一部であり、その上にはL2キャッシュ、さらに上にはL3キャッシュがあります。L1キャッシュは最も近いメモリであり、CPUが必要とするデータが最初に検索される場所です。

機能

  • データのキャッシング: L1キャッシュは、最近使用されたデータや命令を保存することで、CPUが再度そのデータにアクセスする際の待ち時間を短縮します。
  • ヒット率: L1キャッシュの性能は、そのヒット率(CPUがL1キャッシュでデータを見つけられる確率)に大きく依存します。ヒット率が高いほど、CPUはメインメモリへのアクセスを減らし、パフォーマンスが向上します。

まとめ

L1キャッシュは、CPUにおいて最も重要なメモリキャッシュであり、処理速度を向上させるために設計されています。小さな容量と非常に高速なアクセス速度を持ち、データと命令を効率的にキャッシュすることで、CPUの全体的なパフォーマンスを向上させます。L1キャッシュは、コンピュータの動作において非常に重要な役割を果たしています。

koichikoichi

列を圧縮することでCPUのループをはるかに高速に実行できる?
ベクトル化処理のくだり難しくて全然よくわからない。

koichikoichi

ビュー (View)

概要

ビューは、データベースにおける仮想的なテーブルであり、実際のテーブルからのデータを動的に取得するためのクエリです。ビューを使用すると、複雑なクエリを簡素化したり、特定のデータのサブセットを提供したりできます。

特徴

  • 動的なデータ取得: ビューは、基になるテーブルからのデータをリアルタイムで取得します。ビューに対してクエリを実行すると、基になるテーブルから最新のデータが常に取得されます。
  • ストレージなし: ビュー自体はデータを格納しないため、ストレージのオーバーヘッドがありません。実際のデータは基になるテーブルにのみ存在します。
  • 読み取り専用: 一部のビューは更新可能ですが、多くの場合、ビューに対してデータの挿入や更新を行うことはできません。

使用例

CREATE VIEW employee_view AS
SELECT employee_id, first_name, last_name
FROM employees
WHERE department = 'Sales';

マテリアライズド・ビュー (Materialized View)

概要

マテリアライズド・ビューは、ビューの一種ですが、実際にデータを物理的に格納します。マテリアライズド・ビューは、複雑なクエリの結果を事前に計算し、ストレージに保存することで、データの取得を高速化します。

特徴

  • 静的なデータ: マテリアライズド・ビューは、作成時のデータのスナップショットを保存します。そのため、ビューと異なり、基になるテーブルの変更がマテリアライズド・ビューに即座には反映されません。
  • ストレージの使用: マテリアライズド・ビューはデータを保存するため、ストレージのオーバーヘッドがあります。
  • 更新の必要: 基になるテーブルが変更された場合、マテリアライズド・ビューを手動または自動で更新する必要があります。

使用例

CREATE MATERIALIZED VIEW sales_summary AS
SELECT product_id, SUM(sales) AS total_sales
FROM sales
GROUP BY product_id;

更新方法

REFRESH MATERIALIZED VIEW sales_summary;

主な違いのまとめ

特徴 ビュー (View) マテリアライズド・ビュー (Materialized View)
データの格納 なし (仮想的なテーブル) あり (物理的にデータを格納)
データの取得 常に最新のデータをリアルタイムで取得 保存されたスナップショットを取得
ストレージオーバーヘッド なし あり
更新の手間 基になるテーブルの変更が即時反映 手動または自動での更新が必要
使用例 クエリの簡素化 大規模データの集約や分析に有効

まとめ

  • ビューは、基になるテーブルからリアルタイムでデータを取得するための仮想的なテーブルであり、ストレージを使用しません。
  • マテリアライズド・ビューは、クエリの結果を物理的に格納し、高速なデータ取得を可能にしますが、ストレージを消費し、基になるテーブルの変更に対して手動または自動で更新する必要があります。
koichikoichi

専門外ではあるけどもストレージの内部構造について全然知識がないんだなということがわかってよかった。
自分が携わっているサービスが成長したときに出てくるであろうパフォーマンスの問題に対して、事前知識的な部分は多少身についた気がする。時間をおいて、もう一度読みたい。

koichikoichi

4章 エンコーディングと進化

koichikoichi

バイナリエンコーディング

バイナリエンコーディングとは、データを効率的にバイナリ形式に変換して転送や保存を行う手法です。これにより、テキスト形式に比べてデータのサイズを縮小し、速度や効率を向上させることができます。特に、ネットワーク通信やストレージの制約がある環境で使われます。

JSONのバイナリエンコーディング

JSON(JavaScript Object Notation)は、軽量なデータ交換フォーマットですが、テキスト形式であるため非効率な場合があります。そのため、以下のようなバイナリエンコーディング形式がJSONの代替として利用されます。

主なバイナリエンコーディング形式

1. BSON

  • BSON(Binary JSON)は、MongoDBで使われているバイナリ形式で、JSONと互換性のあるデータを効率的にエンコードします。
  • ただし、JSONと比べてサイズが大きくなる場合があります(フィールド名がバイナリとして保存されるため)。

2. MessagePack

  • MessagePackは、JSONと互換性を保ちつつ、バイナリ形式に変換することでサイズを小さくし、効率的にデータをシリアライズ・デシリアライズします。
  • JSONよりも小さなサイズでデータを保存し、パフォーマンスが向上します。

3. Thrift

  • Apache Thriftは、データのシリアライズとRPC(リモートプロシージャコール)機能を提供するフレームワークです。
    Thriftのプロトコルには、データをバイナリ形式でエンコードするためのいくつかのプロトコルがあります。
  • フィールドタグによって、フィールド名を省略してエンコードできます。タグを使用することで、フィールド名そのものをバイナリデータに含める必要がありません。
  • 主なプロトコル:
    • BinaryProtocol: バイナリ形式でシンプルにデータをエンコードします。
    • CompactProtocol: バイナリ形式のデータサイズをさらに縮小するためのプロトコルです。効率的なエンコードが可能です。

4. Protocol Buffers

  • Protocol Buffers(通称「Protobuf」)は、Googleが開発したバイナリエンコーディング形式です。スキーマ定義に基づいてデータをエンコードし、コンパクトなバイナリ形式に変換します。
  • 他のバイナリエンコーディング形式と比較しても、非常に高速でコンパクトなデータ表現が可能です。
  • スキーマに基づいているため、データの拡張やバージョン管理が容易です。
koichikoichi

データベースでは、データベースに書き込みを行うプロセスがデータをエンコードし、データベースから読み取りを行うプロセスがそのデータをデコードします。データベースにアクセスを行うプロセスは1つだけしか存在しないこともありますが、それはライターと同じプロセスがリーダーとして後者の処理も行っているというだけです。この場合、データベースへの保存処理は将来の自分自身へのメッセージ送信と考えられます。

  • モデルオブジェクトは、DBから読み取ったデータをオブジェクトに変換する役割がある。
    • 内部的には、デコードに相当
  • モデルオブジェクトは、オブジェクトのデータをDBに保存するために、データベースの形式に変換する役割がある。
    • 内部的には、エンコードに相当

当たり前といえばそうなのかもしれないが、あまり意識していなかった部分。

koichikoichi

RPC について馴染みが薄いので、もう少し深掘りたい。

koichikoichi

11月1日にACM会員になってOreilly読み放題のサブスク契約する。(トライアル期間が過ぎてしまった)