0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【図解】NoSQL・グラフ・ベクトル…RDB以外のデータベース7種を一気に整理する

0
Posted at

はじめに

「NoSQL」と一括りにされがちですが、その中身はKey-Value、ドキュメント、ワイドカラム、グラフ、ベクトル、時系列、検索エンジンと、性格の全く違うデータベースの寄せ集めです。

この記事は、それぞれのデータベースを「データの形」「得意な検索パターン」「クエリの書き方」の3点セットで一気に整理するチートシートです。実務でDBを選ぶときの「1枚目のマップ」として使えることを目指しました。

なお、この記事は「RDB以外」を扱いますが、多くのシステムでは今もRDBが本命です。「RDBが黙って守ってくれているものは何か」を掘り下げた記事も別途書いています。合わせてどうぞ。

もしもこの世界にRDBがなかったら (5つの根本価値)


0. 全体マップ

早見表

種類 代表エンジン データの形 得意 苦手
RDB PostgreSQL / MySQL / Oracle / SQLite 行と列のテーブル 整合性・複雑なJOIN・トランザクション 半構造データ・水平スケール
Key-Value型 Redis / DynamoDB / Memcached / etcd key → value 超高速な単純検索・キャッシュ 値の中身での検索
ドキュメント型 MongoDB / Firestore / Couchbase JSON文書 柔軟なスキーマ・階層構造 複雑なJOIN・強い整合性
ワイドカラム型 Cassandra / HBase / ScyllaDB / Bigtable 行キー+可変列 大量書き込み・線形スケール 後付けの複雑な検索
グラフ型 Neo4j / Neptune / ArangoDB ノードとエッジ 多段階の関係探索 大量の一括集計
ベクトル型 Pinecone / Qdrant / Weaviate / pgvector 埋め込みベクトル 意味的な類似検索 完全一致・厳密な条件検索
時系列型 InfluxDB / TimescaleDB / Prometheus timestamp+値 連続データの蓄積と集約 更新・削除の多い用途
検索エンジン型 Elasticsearch / OpenSearch / Meilisearch 転置インデックス 全文検索・あいまい検索 トランザクション処理

1. Key-Value型(キーバリューストア)

データ構造イメージ

   ┌──────────────────┬───────────────────────────┐
   │       KEY        │           VALUE           │
   ├──────────────────┼───────────────────────────┤
   │ user:1001:name   │ "Taro Yamada"             │
   │ session:ab12cd   │ {token:..., exp:...}      │  ← 中身はDB側は関知しない
   │ page:home:views  │ 48213                     │
   │ cart:u1001       │ ["item:22","item:87"]     │
   └──────────────────┴───────────────────────────┘

   検索は「キー指定のみ」→ ハッシュ表と同じ O(1) 相当の速さ

アクセスの流れ(キャッシュとしての典型構成)

特徴

  • キーと値のペアだけで管理する、最もシンプルな構造
  • 多くはインメモリ動作で、ミリ秒未満の応答が可能
  • 値の中身では検索できない(=「名前がTaroの人」は探せない)

相性の良い用途

  • セッション情報、認証トークン
  • DBの手前に置くキャッシュ
  • ランキング・カウンター(いいね数、PV数)
  • ショッピングカートのような一時的な状態

クエリ方法:コマンド(API)ベース

# Redis
SET user:1001:name "Taro Yamada"   # キー user:1001:name に文字列を保存
GET user:1001:name                 # キーを指定して値を取得
INCR page:home:views               # このキーの数値を +1(アトミック演算)
EXPIRE session:ab12cd 3600         # 3600秒後に自動削除(TTL指定)

# --- Sorted Set:スコア付きでメンバーを保存し、スコア順に自動で並ぶ特殊な型 ---
ZADD ranking 1500 "player:A"       # スコア1500で player:A を登録
ZADD ranking 2100 "player:B"       # スコアの高い順に内部で自動整列
ZREVRANGE ranking 0 9              # 上位10件を取得

2. ドキュメント型データベース

RDBとの構造比較

ドキュメントごとにフィールドが違ってよい

コレクション "products"
 ┌─────────────────────────────┐  ┌─────────────────────────────┐
 │ _id: "p001"                 │  │ _id: "p002"                 │
 │ name: "小説A"                │  │ name: "ノートPC"             │
 │ author: "山田"               │  │ cpu: "Core i7"              │  ← 項目が
 │ pages: 320                  │  │ memory_gb: 16               │     全然違う
 │                             │  │ ports: ["USB-C","HDMI"]     │
 └─────────────────────────────┘  └─────────────────────────────┘
        同じコレクションに同居できる(スキーマレス)

特徴

  • JSON(BSON)に近い「ドキュメント」単位で保存
  • スキーマが緩やかで、フィールドの追加・省略が自由
  • ネスト構造をそのまま1レコードとして扱えるためJOINが減る
  • シャーディングによる水平分散が前提の製品が多い

相性の良い用途

  • 商品カタログ(商品ごとに項目数が違う)
  • CMS、ブログ記事、ユーザープロフィール
  • Web/モバイルアプリのバックエンド(フロントのJSONとほぼ1対1)
  • スキーマが頻繁に変わる初期フェーズのプロダクト

クエリ方法:JSONライクなクエリ言語

// 検索:SQLの WHERE に相当
db.products.find({ category: "book", price: { $lt: 2000 } })

// 更新
db.products.updateOne({ _id: "p001" }, { $set: { stock: 15 } })

// 集計パイプライン:SQLの GROUP BY に相当
db.orders.aggregate([
  { $match: { status: "completed" } },
  { $group: { _id: "$customerId", total: { $sum: "$amount" } } },
  { $sort:  { total: -1 } }
])

3. ワイドカラム型(列指向分散DB)

データ構造イメージ

   RowKey       │ 列は「行ごとに」違ってよい(数千〜数百万列も可)
   ─────────────┼──────────────────────────────────────────────
   user:001     │ name:太郎 │ age:28 │
   user:002     │ name:花子 │ email:x@y.z │ tel:090-xxxx │ city:千葉
   sensor:t01   │ 2026-07-01T00:00:Z=21.3 │ 00:01:Z=21.5 │ 00:02:Z=21.4 ...
                          ↑ タイムスタンプそのものが列名になる設計もよく使う

Key-Value型・ドキュメント型との違い

一言で言うと、「Key-Valueに、DBが理解できる列構造をつけて、超巨大にスケールできるようにしたもの」 がワイドカラム型です。

観点 Key-Value型 ドキュメント型 ワイドカラム型
キーで引ける単位 1つの値(ブロブ) 1つのJSON文書 1つの行(=複数の名前付き列の集合)
DBは値の中身を理解しているか しない(不透明なバイト列) JSON構造を理解 列の名前と型を理解
「1レコード内の一部だけ」を読める 不可(値まるごと取得) 部分投影は可能 特定の列だけ読める(列指向の恩恵)
スキーマ なし 緩い(文書ごとに違ってOK) 緩い(行ごとに違う列を持ってOK、ただし列は事前定義)
ネスト構造 値の中に任意に埋められる(DBは解釈しない) 得意(深い階層OK) 苦手(基本フラットな行×列)
主な使いどころ キャッシュ・セッション Web/モバイルのメイン格納 超大量書き込み・時系列・イベントログ
データ量の想定 数GB〜数TB 〜数TB 数十TB〜PB級(数百ノードで水平分散)
クエリの自由度 キー完全一致のみ 値の中身で柔軟に検索できる 事前に決めたパターンでしか引けない(クエリファースト設計)

イメージで捉えるなら:

  • Key-Valueは「巨大なハッシュ表」→ とにかく速いが、値の中身は問わない
  • ドキュメントは「JSONの倉庫」→ 値の中身で柔軟に検索できる
  • ワイドカラムは「果てしなく横に広い、超巨大なExcel」→ 行ごとに列は違ってよく、書き込み量とデータサイズが桁違い(PB級)

分散のイメージ

ワイドカラム型の最大の武器は「ノードを追加するだけで性能が線形に伸びる」水平スケーラビリティです。

図の読み方:

  • RowKey(例: user:001)= 1行を一意に識別するキー。RDBの主キーに相当
  • ハッシュ関数 = RowKeyを 0.00〜1.00 の数値に変換する関数(同じキーは必ず同じ数値になる)
  • 各ノードは「担当するハッシュ範囲」を持ち、その範囲に落ちる行だけを保存
  • ノードを追加すると担当範囲が細分化され、既存データの一部だけが移動する → 無停止で線形にスケール

特徴

  • 「行キー+列ファミリー」構造で、行ごとに異なる列を持てる
  • 大量の書き込みと、ノード追加による線形スケールアウトに強い
  • 一般に強整合性より可用性を優先(結果整合性)
  • クエリファースト設計: 「どう読み出すか」を先に決めてテーブルを作る(後から自由な条件検索はできない)

相性の良い用途

  • IoTセンサーデータ、大量のログデータ
  • メッセージングアプリの履歴
  • レコメンド用の行動ログ
  • 巨大なイベントストリーム

クエリ方法: CQL(SQL風だがJOINなし)

CREATE TABLE sensor_data (
  sensor_id  text,
  event_time timestamp,
  value      double,
  PRIMARY KEY (sensor_id, event_time)   -- ← 検索パターンをここで固定する
);

SELECT * FROM sensor_data
WHERE sensor_id = 'temp-01'
  AND event_time > '2026-07-01';

WHERE value > 30 のようなパーティションキー外の条件は原則NG。必要なら別テーブルを作る。


4. グラフデータベース

データ構造イメージ

上図で「田中さんにおすすめの商品」を考えると、田中 → 佐藤(友人)→ 商品B という2ホップの経路が一瞬でたどれます。

RDBとの決定的な違い

■ RDB で「友達の友達の友達」を探す
   users ⋈ friends ⋈ friends ⋈ friends  ← JOINが3段
   段数が増えるほど、探索対象が指数的に膨らんで急激に遅くなる

   1段: ▓▓                      速い
   2段: ▓▓▓▓▓▓▓▓                やや遅い
   3段: ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓  かなり遅い

■ グラフDB(インデックスフリー隣接)
   各ノードが「隣のノードへのポインタ」を直接持つ
   → 1ホップ進むコストがほぼ一定

   1段: ▓▓
   2段: ▓▓▓
   3段: ▓▓▓▓                    段数が増えてもほぼ線形

補足: インデックスフリー隣接(Index-Free Adjacency)

グラフDBが「関係の探索」で速い理由の核心です。

RDBで「田中さんの友達」を探す場合:

1. usersテーブルから「田中」の id を探す         ← インデックスを引く(O(log n))
2. friendsテーブルで user_id = 田中のid の行を探す ← インデックスを引く(O(log n))
3. さらに usersテーブルで friend_id を引き直す     ← インデックスを引く(O(log n))

1ホップ進むたびに 「インデックスを引く」コスト(対数時間 O(log n)) がかかります。データが増えるほどインデックス自体も肥大化して遅くなります。

グラフDBの場合:

   [田中さんノード]
       │  ← このノード自身に「隣接ノードへの物理ポインタ(リスト)」が
       ▼     直接くっついている(=インデックスを介さない)
   ┌─ 友達ポインタ ──> [佐藤さんノード]
   ├─ 友達ポインタ ──> [吉田さんノード]
   └─ 購入ポインタ ──> [商品Aノード]

各ノードのレコード内に「隣のノードがディスク上のどこにあるか」を示すポインタが直に埋め込まれているため、次のノードへ進むコストは常に一定(O(1))。何段辿ってもインデックス検索は発生しません。

これが「インデックスを引かずに(=フリーに)隣(Adjacency)へ行ける」= インデックスフリー隣接。グラフDBが多段の関係探索でRDBに圧勝する根本原因は、この物理レイアウトの違いにあります。

なお、RDBのインデックスがなぜ「引くたびに O(log n)」になるのかは、B-treeの内部構造を可視化した別記事で扱っています。

RDBインデックスの動く図解 (B-tree / ハッシュ / 複合 / カバリング)

相性の良い用途

  • SNSの友人・フォロー関係
  • レコメンドエンジン(多段階の「買った人はこれも」)
  • 不正検知(送金ネットワークの怪しいつながり)
  • 組織図、サプライチェーン、知識グラフ

クエリ方法: Cypher(図をそのまま書くような構文)

// 「田中さんの友達の友達」
MATCH (me:Person {name: "田中"})-[:FRIEND]->(:Person)-[:FRIEND]->(fof:Person)
WHERE fof <> me
RETURN DISTINCT fof.name
構文の読み方:
   (me:Person)  -[:FRIEND]->  (fof:Person)
     ↑ノード        ↑エッジ        ↑ノード
   ()が丸いノード、-[]->が矢印つきエッジ。図の見た目とほぼ同じ。
// レコメンド: 友人が買っていて自分が買っていない商品
MATCH (me:Person {name:"田中"})-[:FRIEND]->(f)-[:PURCHASED]->(p:Product)
WHERE NOT (me)-[:PURCHASED]->(p)
RETURN p.name, count(*) AS score ORDER BY score DESC

他に Gremlin、SPARQL(RDF向け)といったクエリ言語もあります。


5. ベクトルデータベース

データ構造イメージ

テキスト・画像 ──[埋め込みモデル]──> ベクトル(数百〜数千次元の数値配列)

  "犬がボールで遊んでいる"  →  [0.21, -0.88, 0.05, ... , 0.42]   (1536次元)
  "子犬がおもちゃで遊ぶ"    →  [0.19, -0.85, 0.07, ... , 0.40]   ← 数値が近い!
  "四半期の決算報告"        →  [-0.72, 0.31, 0.90, ... ,-0.11]   ← 全然違う

検索の考え方(ベクトル空間を2次元に投影したイメージ)

     ▲
     │        ● 子犬          意味が近いものは
     │     ● 犬               空間上でも近くに集まる
     │   ★ ←── クエリ「わんこ」
     │      ● 柴犬
     │
     │                              ● 決算
     │                          ● 売上高
     │                            ● 損益計算書
     └──────────────────────────────────────▶

     ★から距離が近い順に top_k 件を返す = 類似検索
     距離の測り方: コサイン類似度 / 内積 / ユークリッド距離

RAG(LLMの知識検索)での使われ方

特徴

  • テキスト・画像・音声を埋め込みベクトルに変換して保存
  • 完全一致ではなく「意味的に近いもの」を探す近似最近傍探索(ANN)
  • HNSW / IVF などのインデックスにより、数百万〜数十億件からミリ秒〜数十msで検索
  • メタデータフィルタ(例: カテゴリ=家電のものだけ)と組み合わせ可能

相性の良い用途

  • LLMのRAG基盤(社内文書検索、FAQ、マニュアル検索)
  • 画像・音声の類似検索
  • レコメンド(ユーザー・商品の埋め込み類似度)
  • セマンティック検索(キーワードが一致しなくても意味で探す)
  • 異常検知(正常データの分布から離れたベクトルを検出)

クエリ方法

# Pinecone(専用API型)
results = index.query(
    vector=query_embedding,        # 質問を埋め込んだベクトル
    top_k=5,
    filter={"category": "manual"}, # メタデータで絞り込み
    include_metadata=True
)
-- pgvector(PostgreSQL拡張型): SQLの中でベクトル検索できる
--
-- ▼ <=> について
-- <=> は pgvector が追加した「2つのベクトル間のコサイン距離」を計算する演算子。
--   ・返り値:  0 〜 2 の数値
--       0     → 完全に一致(=意味がそっくり)
--       1     → 直交(=無関係)
--       2     → 真逆
--   ・値が小さいほど「意味的に近い」と解釈する
--
-- 他の距離演算子:
--       <->   ユークリッド距離
--       <#>   内積のマイナス(大きい類似度ほど小さい値にするため)
SELECT id, content,
       embedding <=> '[0.12, 0.98, ...]' AS distance
FROM documents
WHERE category = 'manual'          -- 通常のSQL条件と併用できるのが強み
ORDER BY embedding <=> '[0.12, 0.98, ...]'
LIMIT 5;

6. 時系列データベース

データ構造イメージ

CPU使用率(%)
100 │
    │              ╭╮
 75 │        ╭─╮  ╭╯╰╮
 50 │   ╭────╯ ╰──╯  ╰──╮      ╭────
 25 │╭──╯                ╰──────╯
  0 └──────────────────────────────────▶ time
     00:00   06:00   12:00   18:00

   保存されるのは「timestamp + tag + value」の連続レコード
   ┌────────────────────┬───────────┬───────┐
   │ time               │ host      │ value │
   ├────────────────────┼───────────┼───────┤
   │ 2026-07-27T09:00:00│ web-01    │ 42.1  │
   │ 2026-07-27T09:00:10│ web-01    │ 44.8  │  ← 追記のみ、更新しない
   │ 2026-07-27T09:00:20│ web-01    │ 43.2  │
   └────────────────────┴───────────┴───────┘

データのライフサイクル

特徴

  • 「タイムスタンプ+値」に特化、データは基本的に追記のみ
  • 自動的なダウンサンプリングと保持期間ポリシーを標準搭載
  • 圧縮効率が高く、大量の連続データを低コストで長期保存できる

相性の良い用途

  • サーバー監視メトリクス(CPU、メモリ、レスポンスタイム)
  • IoTセンサー値(温度、湿度、位置情報)
  • 株価・為替のティックデータ
  • アプリケーションパフォーマンス監視(APM)

クエリ方法

-- TimescaleDB(PostgreSQL拡張): SQLがそのまま使える
SELECT time_bucket('1 hour', event_time) AS bucket,
       avg(cpu_usage), max(cpu_usage)
FROM metrics
WHERE event_time > now() - interval '1 day'
GROUP BY bucket
ORDER BY bucket;
# Prometheus PromQL: 関数型のクエリ言語
rate(http_requests_total[5m])                 # 5分間の増加率
avg_over_time(cpu_usage{host="web-01"}[1h])   # 1時間平均

7. 検索エンジン型データベース

転置インデックスのしくみ

■ 元の文書
   doc1: "軽量なノートパソコンが欲しい"
   doc2: "ノートに書く"
   doc3: "パソコンの修理"

■ 形態素解析して転置インデックスを作る(単語 → 文書リスト)
   ┌────────────┬──────────────────┐
   │   単語     │   出現する文書    │
   ├────────────┼──────────────────┤
   │ 軽量       │ doc1             │
   │ ノート     │ doc1, doc2       │
   │ パソコン   │ doc1, doc3       │
   │ 修理       │ doc3             │
   └────────────┴──────────────────┘

■ 「ノートパソコン」で検索
   ノート → {doc1, doc2}
   パソコン → {doc1, doc3}
   両方含む doc1 のスコアが最も高くなる → 関連度順に返却

検索リクエストの流れ

特徴

  • 転置インデックスにより全文検索に特化
  • 表記ゆれ、あいまい検索、関連度スコアリング、ファセット集計が得意
  • ログの集約・可視化基盤としても広く使われる(Kibana等と併用)

相性の良い用途

  • ECサイトの商品検索(あいまい検索・関連度順表示)
  • ログ分析基盤
  • ドキュメント検索、サポートFAQ検索

クエリ方法: JSON形式のクエリDSL

{
  "query": {
    "bool": {
      "must":   { "match": { "title": "ノートパソコン" } },
      "filter": { "range": { "price": { "lte": 100000 } } }
    }
  },
  "aggs": {
    "by_brand": { "terms": { "field": "brand" } }
  }
}

8. 選定フローチャート


9. 実務では「組み合わせる」のが普通

1つに絞らず複数のDBを併用する設計を Polyglot Persistence(多言語永続化) と呼びます。以下は社内ドキュメント検索を含むWebサービスの典型構成です。

併用時の注意点

論点 内容
Source of Truth 「正となるデータ」はどれか1つに決める。上図ではPostgreSQL
同期のラグ 検索用インデックスは通常あとから追いつく。即時反映が必要かを確認する
運用コスト DBが増えるほど監視・バックアップ・障害対応の対象が増える
まず1つで試す 小規模なら pgvector や PostgreSQLの全文検索で足りることも多い

まとめ

  • RDBは万能だが、データの「形」とアクセスパターンによっては非RDBが桁違いに効率的
  • 特に近年は、LLM活用に伴う ベクトルDB と、関係性の分析を担う グラフDB の重要性が上がっている
  • 選定は「速いDBはどれか」ではなく「自分のデータの形に合った構造はどれか」で考える
  • 実務では単一DBに絞らず、役割ごとに使い分けるのが一般的

そして最後に強調しておきたいのは、この記事で並べた7種類の非RDBがいくら強力でも、「正となるデータの置き場所(Source of Truth)」は今もほとんどの場合RDBだという事実です。上のPolyglot構成図でも、PostgreSQLだけが太枠で囲まれています。ワイドカラム型は結果整合性、Redisは揮発、ドキュメント型はトランザクション制約が弱い──それぞれ「速さ」や「柔軟さ」の裏で、RDBが黙って守ってくれている何かを手放しています。

「その手放しているものが具体的に何なのか」を、Excelで壊れる7つの事例から掘り下げた記事もあります。

もっと詳しく:


バグ・ご意見は X @taitech_dev まで。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?