Skip to content

NoSQLデータモデリング 解説

ドキュメント、キーバリュー、ワイドカラム、グラフ、ベクトルストアのモデリングパターン — いつ埋め込み、参照、非正規化、タイムバケット化するか。

NoSQLストアは固定スキーマとJOINを水平スケールと引き換えにする。ファミリーごとにリレーションの表し方が異なるため、構造を決めるのは正規形ではなくアクセスパターン。

リファレンステーブル · 28 項目
28 of 28 rows
ドキュメント
入れ子のサブドキュメントが、独立したテーブルではなく親ドキュメント1つの内部に置かれる。一緒に読まれる1対少数のリレーション。明細行を伴う注文。3つの住所を持つユーザー。
データではなく外部idを保存する。2回目の読み取りか$lookupで解決する。大きい・共有される・単独で更新されるサブエンティティ。コレクション横断の多対多リンク。
親が子の短い配列を埋め込み、集合全体が1つのドキュメントとして収まり、1回で読み込まれる。プロフィール上の数件の電話番号や設定。1回の読み取りで親と子を同時に取得。
子は専用のコレクションに置く。各子は親idを持ち、または親は上限つきid配列を保持する。埋め込むには大きすぎる・独立しすぎている集合。親idで子を検索。
子が親idを保存し、親は子を列挙しない。集合が上限なく成長するため。ホストやユーザーに紐づくイベント、ログ、投票。親idで子を絞り込んでページネーション。
異なるエンティティ型のドキュメントが1つのコレクションを共有し、typeフィールドで区別される。多様な親に紐づくコメントや通知を、同じクエリ1つで検索する。
少数の極端なエンティティを共有の形から分離し、そのサイズやトラフィックが他を歪めないようにする。フォロワーリストやカウンターが通常ドキュメントの大きさを超えてしまうセレブリティアカウント。
各リビジョンは同じidと増加するバージョン番号を持つ新ドキュメント。クエリが最新を選ぶ。旧リビジョンが検索可能なまま残る監査証跡と編集履歴。
派生値を書き込み時に計算し、要約対象のデータの隣に保存する。そのままでは毎読み取り時に集計が走る合計、評価、トップN。
キーバリュー・ワイドカラム
DynamoDBの設計用語:パーティションキーとソートキーをオーバーロードし、多数のエンティティ型を1テーブルに集約する。JOINなしで1クエリで異種アイテムを取得。
1つのキー属性が複数のエンティティ型を兼ねる。USER#やORDER#などのプレフィックスで区別する。シングルテーブル設計。関連アイテムを1パーティションに集め、1クエリで取得。
パーティションキーが行をノードへ分散し、ソートキーが各パーティション内を並べる。CassandraとDynamoDB。範囲スキャン、順序付きログ、階層的な複合キー。
複数属性を区切り文字つきの1つのソートキーに連結 — country#region#city — し、プレフィックス一致で階層を下る。階層と多面的フィルター。順序付き範囲クエリ1本が多数のインデックス検索を置き換える。
イベントを固定ウィンドウ — 時・日・月 — に専用パーティションやテーブル単位でグループ化する。メトリクス、ログ、IoTテレメトリ。パーティションサイズを抑え、コールドデータを期限切れにする。
読み取りの多いフィールドをレコード間で複製し、クエリが2回目の検索を追わないようにする。結果整合が許容されストレージが安い、読み取り中心のワークロード。
一部のアイテムだけが持つ属性の上に構築したセカンダリインデックス。持たないアイテムはインデックスに入らない。代替のアクセス経路。フルスキャンなしでスパースな部分集合 — 未完了注文、未出荷アイテム — を見つける。
まず正確なクエリを書き下ろす。テーブル配置はエンティティや正規形からではなく、そこから導く。CassandraとDynamoDBのスキーマ設計。クエリファミリーごとに1テーブル。データモデルが問いを写す。
問いに合致するキーがないため、クエリが多数のパーティションやノードに並行ヒットせざるを得ない状態。目標ではなく設計で排除すべき悪臭。よくあるクエリが1パーティションに載るようキーを作り直す。
書き込み時に各読み手のビューへ複製を配るか、読み取り時に収集してマージする。フィードとタイムライン。小さいオーディエンスには書き込み時、巨大には読み取り時。
事前構築済みのクエリ結果を独自テーブルとして保存し、ベースの変化に合わせて最新を保つ。1つの書き込み経路の上の代替読み取り形。ベーステーブルが答えられないクエリに応える。
グラフ
各辺が、始点ノードから終点ノードへのポインタとして行やタプルで表される。グラフ・プロパティストア。友達の友達検索、経路探索、レコメンデーション。
各リレーションが専用コレクションの1ドキュメントで、from/toのidとリンク属性を運ぶ。ドキュメントストア内のグラフ。リンク自体への属性 — 評価、役割、タイムスタンプ。
各ノードが深さ優先巡回の左・右カウンタを持ち、部分木は親の範囲内に収まる。読み取り中心のツリー。BETWEEN 1本で部分木を取得。挿入は巡回の番号を振り直す。
専用テーブルが祖先-子孫の全ペアを、深さ0の自己ペアも含めて記録する。任意の深さの全ブランチクエリ。シンプルなJOINを、ストレージとメンテナンスで支払う。
プロパティグラフはノードと辺にキーバリュー属性を付け、RDFはすべてを主語-述語-目的語のトリプルで述べる。リッチなトラバーサルにはプロパティグラフ(Neo4j)、リンクトオープンデータとオントロジー交換にはRDF。
ベクトル
高次元のfloat配列がメタデータの隣に置かれる。ANNインデックスが最近傍を見つける。セマンティック検索、RAG検索、レコメンデーション、非構造化コンテンツの類似度。
元ドキュメントを — サイズ、オーバーラップ、構造の基準で — 分割し、各断片を個別に埋め込む。RAG検索の品質。チャンクサイズは精度とリコールをトレードオフする。オーバーラップは分割された文を保護する。
密ベクトル類似度がキーワード一致と並走し、2つのランキングリストを1つの結果へ融合する。正確な語と意味を同時に必要とし、さらにメタデータフィルタを重ねるクエリ。