- データベース
NoSQLとは|RDBとの違い・4種類をRedis/PostgreSQL実測で解説
結論
NoSQLとは、「表(テーブル)と SQL」を前提にしないデータベースの総称です。キーバリュー型・ドキュメント型・列指向型・グラフ型の4種類があり、RDB(リレーショナルデータベース)より「決まった形のデータを、決まった引き方で、大量に・速く」扱うことに向きます。複雑な結合や厳密な整合性が要る業務データは RDB のままが向きます。
この記事でわかること
- NoSQL と RDB の違い、4種類の違いを表と図で
- 同じ商品データを Redis・SQLite・PostgreSQL に入れて取り出し、10万件で検索時間を実測した結果
- トランザクション・レプリカ(CAP)・スキーマレスの落とし穴を実際のエラー原文で
RDB は「行と列の表」にデータを入れ、SQL で結合・集計します。NoSQL は、表以外の形(キーと値・JSON の文書・列の束・点と線)でデータを持ち、その形に合った引き方だけを速くします。
flowchart TD
D[(データベース)] --> R[RDB<br>表と SQL]
D --> N[NoSQL<br>表以外の形]
N --> T
subgraph T[NoSQL の4種類]
direction TB
K[キーバリュー型<br>Redis・DynamoDB] ~~~ C[ドキュメント型<br>MongoDB・Firestore]
C ~~~ W[列指向型<br>Cassandra・Bigtable]
W ~~~ G[グラフ型<br>Neo4j・Neptune]
end
よく調べられることの答えを先にまとめます。
| 知りたいこと | 答え | 詳しい節 |
|---|---|---|
| NoSQLとは | 表と SQL を前提にしないデータベースの総称(Not only SQL) | NoSQLとは |
| NoSQL と RDB の違い | データの形・スキーマ・拡張の方向・整合性の保証が違う | NoSQLとは |
| NoSQL の種類 | キーバリュー・ドキュメント・列指向・グラフの4種類 | NoSQLの4種類 |
| NoSQL は速いのか | キーで引くのは速い。条件検索は索引を自分で作る | 10万件で実測 |
| スキーマレスとは | 列を決めずに入れられる。型の不揃いは自分で防ぐ | スキーマレスの意味と落とし穴 |
| CAP 定理とは | 分断時に「整合性」と「可用性」のどちらを守るかの選択 | CAP定理と結果整合性 |
| RDB で JSON を扱える? | PostgreSQL の jsonb・SQLite の JSON 関数で扱える | RDBでNoSQL的に使う |
| どちらを選ぶか | 結合・集計・厳密な整合性が要るなら RDB。それ以外は判断表で | NoSQLが向く・向かない |
確認環境:Ubuntu 24.04.5 LTS の上で、Redis 7.0.15(Ubuntu のパッケージ)を自分の設定ファイルで 127.0.0.1:46379 に起動し、同じ手順でレプリカを 127.0.0.1:46380 に起動しました。PostgreSQL 16.15(Ubuntu のパッケージ)はデータベース t15db を作って使い、SQLite は Python 3.13.18 の sqlite3 モジュール(SQLite 3.45.1)で動かしています。Python からの接続は redis 8.1.0・psycopg 3.3.6、コマンドラインは redis-cli 7.0.15・psql 16.15 です。MongoDB・Cassandra・Neo4j はこの環境に入れられなかったため、公式ドキュメントの例と記述の範囲で書いています。図は mermaid で描画を確かめています(確認日 2026年10月29日)。
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「NoSQLとは|RDBとの違い・4種類をRedis/PostgreSQL実測で解説」
NoSQLとは(RDBとの違いの一覧表)
この節では、NoSQL という言葉の意味と、RDB と何が違うのかが一覧で分かります。
NoSQL は、リレーショナルモデル(行と列の表を関係で結ぶ考え方)以外の方法でデータを管理するデータベースの総称で、「Not only SQL(SQL だけではない)」と読むのが一般的です。 特定の製品の名前ではなく、Redis・MongoDB・Cassandra・Neo4j のように作りも使い方も違うものをまとめて呼んでいます。「SQL を使わない」という意味ではなく、Cassandra の CQL のように SQL に似た言語を持つものもあります。
| RDB | NoSQL | |
|---|---|---|
| データの形 | 行と列の表。表どうしを外部キーで結ぶ | キーと値・JSON の文書・列の束・点と線 |
| スキーマ | 先に列と型を決める(CREATE TABLE) | 決めずに入れられるものが多い(スキーマレス) |
| 引き方 | SQL。任意の列で検索・結合・集計 | キーや索引で引く。製品ごとの API や言語 |
| 拡張 | 1台を大きくする(垂直)。読み取りはレプリカで分散 | 台数を増やす(水平)前提の設計が多い |
| 整合性 | ACID のトランザクション | 製品による。結果整合性(後で揃う)を選べるものが多い |
| 向くデータ | 会計・在庫・受発注など、矛盾が許されないもの | セッション・ログ・カタログ・SNS など、量と速さが要るもの |
データベースそのものの基礎は、関連記事「データベースとは|種類・Excelとの違い・SQLの基本を実例で解説」で扱っています。
「NoSQL のほうが新しくて速い」ではない
NoSQL が速いのは「キーで1件引く」「同じ形のデータを大量に書く」といった、製品が得意とする引き方に限ったときです。後の節の実測では、索引の無い条件検索は Redis が SQLite の 2,000 倍以上遅くなりました。速さは製品の優劣ではなく、データの形と引き方が合っているかで決まります。
NoSQLの4種類(キーバリュー・ドキュメント・列指向・グラフ)
この節では、4種類がそれぞれどんな形でデータを持ち、何に向くのかが分かります。
4種類は「データをどんな形で持つか」で分かれます。 Google Cloud の解説ではインメモリ型(Redis・Valkey)を5つ目として分けていますが、データの形としてはキーバリュー型です。
1キーバリュー型
キー(名前)で値を1つ引く。辞書と同じ。値の中身は見ない。セッション・キャッシュ・カウンター・ランキング。Redis・Valkey・Memcached・DynamoDB。
2ドキュメント型
JSON のような文書を1件として持ち、中の項目で検索できる。商品カタログ・ユーザープロフィール・CMS。MongoDB・Couchbase・Firestore。
3列指向(ワイドカラム)型
行キーごとに「列の束」を持ち、台数を増やして横に広げる。時系列・ログ・IoT の大量書き込み。Cassandra・HBase・Bigtable。
4グラフ型
点(ノード)と線(関係)で持ち、「友達の友達」のようにたどる。SNS・推薦・不正検知・経路。Neo4j・Amazon Neptune。
| 種類 | 1件の形 | 得意な引き方 | 苦手なこと |
|---|---|---|---|
| キーバリュー | product:2 → 値 |
キーで1件を引く(マイクロ秒) | 値の中身で検索する |
| ドキュメント | { name: "マウス", spec: {…} } |
項目で検索、入れ子の構造をそのまま | 文書をまたぐ結合・集計 |
| 列指向 | 行キー → 列の束(パーティション) | キー順の範囲読み・大量書き込み | キー以外の条件での検索 |
| グラフ | (人)-[:知人]->(人) | 関係を何段もたどる | 全件の集計・大量の単純書き込み |
同じデータをRedis・SQLite・PostgreSQLに入れて取り出す
この節では、同じ3件の商品データを、キーバリュー型・RDB の表・RDB の JSON 型に入れたときの書き方と戻り値の違いが分かります。
入れるデータは同じでも、「どう引けるか」が最初から違います。 商品3件(名前・価格・タグ・仕様)を Python から入れて取り出しました。
PRODUCTS = [
{"id": 1, "name": "ノートPC", "price": 128000, "tags": ["pc", "work"], "spec": {"cpu": "8core", "ram_gb": 16}},
{"id": 2, "name": "マウス", "price": 2980, "tags": ["pc"], "spec": {"wireless": True}},
{"id": 3, "name": "モニター", "price": 34800, "tags": ["pc", "display"], "spec": {"inch": 27, "hz": 144}},
]
# Redis:キーに JSON 文字列、またはハッシュ型で入れる
r.set(f"product:{p['id']}", json.dumps(p, ensure_ascii=False))
r.hset(f"product:{p['id']}:h", mapping={"name": p["name"], "price": p["price"]})
# SQLite:列を決めた表に入れる
con.execute("CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT NOT NULL, price INTEGER NOT NULL)")
# PostgreSQL:表 と jsonb の両方に入れる
cur.execute("CREATE TABLE products_doc (id int PRIMARY KEY, doc jsonb NOT NULL)")
=== Redis(キーバリュー)===
GET product:2 -> {"id": 2, "name": "マウス", "price": 2980, "tags": ["pc"], "spec": {"wireless": true}}
HGETALL product:2:h -> {'name': 'マウス', 'price': '2980'}
TYPE product:1 / product:1:h -> string hash
=== SQLite(RDB・表)===
SELECT * WHERE price < 50000 -> [(2, 'マウス', 2980), (3, 'モニター', 34800)]
列を増やさずに入れると -> OperationalError: table products has no column named color
=== PostgreSQL(表 と jsonb)===
表 WHERE price<50000 -> [(2, 'マウス'), (3, 'モニター')]
jsonb WHERE price<50000 -> [('マウス', None), ('モニター', '144')]
jsonb @> tags に display -> [(3,)]
jsonb に color を足して入れた -> ('black',)
表に color を入れると -> column "color" of relation "products" does not exist
Redis(キーバリュー)
product:2 と名前を知っていれば一発で引ける
「価格が5万円未満」はこのままでは引けない
ハッシュ型の値はすべて文字列('2980')
SQLite・PostgreSQL(表)
WHERE で任意の列を条件にできる
決めていない列(color)は入らずエラー
jsonb 列なら、決めていない項目も入る
キーバリュー型(Redis)でできること・できないこと
この節では、キーバリュー型の代表である Redis で、値をどう持ち、キー以外で引きたいときにどうするのかが分かります。
キーバリュー型は「キーが分かっていれば速い」「キーが分からなければ引けない」が基本です。 値で探したいときは、索引にあたるキーを自分で作ります。redis-cli で入れた結果です。
127.0.0.1:46379> GET product:2
"{\"id\":2,\"name\":\"マウス\",\"price\":2980}"
127.0.0.1:46379> GET product:9 # 無いキー
(nil)
127.0.0.1:46379> SADD tag:display 3 # タグ → id の集合(索引を自分で作る)
(integer) 1
127.0.0.1:46379> SINTER tag:pc tag:display
1) "3"
127.0.0.1:46379> ZADD price 128000 1 2980 2 34800 3 # 価格 → id の順序つき集合
(integer) 3
127.0.0.1:46379> ZRANGEBYSCORE price 0 50000 WITHSCORES
1) "2"
2) "2980"
3) "3"
4) "34800"
127.0.0.1:46379> SET session:abc123 "user=1" EX 60 # 60秒で消える
OK
127.0.0.1:46379> TTL session:abc123
(integer) 54
127.0.0.1:46379> INCR pageview:top
(integer) 2
127.0.0.1:46379> RPOP queue:mail
"job2"
| やりたいこと | RDB なら | Redis では |
|---|---|---|
| id=2 を引く | WHERE id = 2 |
GET product:2 |
| 価格が5万円未満 | WHERE price < 50000 |
ZSET に価格を入れておき ZRANGEBYSCORE |
| タグが pc かつ display | WHERE … AND … |
タグごとの SET を SINTER |
| 価格の範囲かつタグ | 1つの WHERE | 2つの結果をアプリ側で突き合わせる |
| 60秒で消える値 | 期限の列と削除のジョブ | SET … EX 60 |
注文と商品のように2つのデータを結び付けるときも、SQL の JOIN にあたる命令は無く、アプリ側で2回引きます。手元では、注文3件それぞれについて HGETALL order:101 → HGETALL product:1 と引いて、SQL の JOIN と同じ結果(101: ノートPC 128000 / 102: マウス 8940 / 103: マウス 2980)を組み立てました。件数が増えると、この「1件ごとに引く」回数がそのまま往復の回数になります。Redis のデータ型と用途は、関連記事「Redisとは|何に使う・なぜ速いかをRDBとの実測で解説【用途早見表】」で扱っています。
ドキュメント型・列指向型・グラフ型の書き方(公式ドキュメントで)
この節では、この環境に入れられなかった3種類について、公式ドキュメントにある書き方と、SQL との対応が分かります。
ドキュメント型:MongoDB
MongoDB の公式ドキュメント(SQL to MongoDB Mapping Chart)は、用語を次のように対応させています。
| SQL | MongoDB |
|---|---|
| table | collection |
| row | document(BSON document) |
| column | field |
| table joins | $lookup、または埋め込みドキュメント |
| primary key | _id フィールド(自動で設定される) |
// 公式の対応例(SQL to MongoDB Mapping Chart)
// SELECT * FROM people WHERE status = "A"
db.people.find({ status: "A" })
// INSERT INTO people(user_id, age, status) VALUES ("bcd001", 45, "A")
db.people.insertOne({ user_id: "bcd001", age: 45, status: "A" })
// 戻り値の例(insertOne のリファレンス):{ acknowledged: true, insertedId: 10 }
// UPDATE people SET status = "C" WHERE age > 25
db.people.updateMany({ age: { $gt: 25 } }, { $set: { status: "C" } })
公式ドキュメントは、存在しないコレクションに insert するとコレクションが作られること(「Inserting a document into a collection that doesn't exist creates that collection.」)、_id を省くとドライバーが ObjectId を自動で付けることを明記しています。CREATE TABLE にあたる手順が無い、というのがドキュメント型の入口です。mongosh の操作は、関連記事「MongoDB入門|mongoshのコマンド一覧とSQLとの対応表」で扱っています。
列指向型:Cassandra(CQL)
Cassandra の CQL は SQL に似た見た目です。公式ドキュメントの例では、INSERT INTO NerdMovies JSON '{"movie": "Serenity", "director": "Joss Whedon", "year": 2005}'; のように JSON で1行を入れられ、SELECT は SELECT name, occupation FROM users WHERE userid IN (199, 200, 207); のように書きます。違いは WHERE の自由度で、公式ドキュメントは WHERE について「PRIMARY KEY の列か、セカンダリインデックスを定義した列」の関係を指定するものだと説明し、「既定では、CQL は全パーティションの走査を伴わない SELECT だけを許可する」としています。キー以外の列で自由に絞り込めない代わりに、キーで分けた単位(パーティション)を多くの台に散らして大量の書き込みを受けられる、というのが列指向型の作りです。
グラフ型:Neo4j(Cypher)
Neo4j の Cypher は、関係を矢印の絵で書きます。公式ドキュメント(MATCH)の例です。
MATCH (wallstreet:Movie {title: 'Wall Street'})<-[:ACTED_IN]-(actor)
RETURN actor.name
「映画 Wall Street に ACTED_IN で結ばれた俳優の名前」を返します。SQL なら映画・出演・俳優の3表を JOIN する問い合わせで、段数が「友達の友達の友達」と増えるほど、SQL は JOIN が増え、グラフ型は矢印を1つ足すだけになります。
NoSQLは速いのか:10万件で実測(主キー・範囲・結合)
この節では、同じ10万件の商品データで、主キー検索・範囲検索・条件の組み合わせにかかった時間が分かります。
「キーで1件引く」は Redis・SQLite・PostgreSQL のどれも速く、差が出るのは「条件で探す」ときです。 同じマシンの上で、Redis(ネットワーク経由)・SQLite(同じプロセスのファイル)・PostgreSQL(ネットワーク経由。表と jsonb)に10万件を入れ、Python から測りました(1回の実行。環境で変わるので比率を見ます)。
| 操作(10万件) | Redis 7.0 | SQLite 3.45 | PostgreSQL 16 表 | PostgreSQL 16 jsonb |
|---|---|---|---|---|
| 10万件を入れる | 3,296 ms(pipeline・索引込み) | 85 ms(1トランザクション) | 173 ms(COPY) | 306 ms(COPY) |
| 主キーで1万回引く | 878 ms(1回ずつ)/83 ms(pipeline) | 51 ms | 968 ms | 1,228 ms |
| 価格 1000〜2000 の件数(474件)・索引なし | 8,899 ms(SCAN+GET) | 4.2 ms | 13.2 ms | 40.6 ms |
| 同じ検索・索引あり | 4.9 ms(ZSET→GET) | 0.1 ms | 1.2 ms | 1.1 ms(式索引) |
| tag=display かつ 価格 1000〜2000(104件) | 31.7 ms(SET と ZSET をアプリで突合) | 0.9 ms(WHERE AND) | — | 12.8 ms(tag のみ。GIN) |
サーバー本体の速さは redis-benchmark -n 100000 -c 50 で SET 83,125 req/s・GET 96,432 req/s(p50 0.27ms)でした。表の「1回ずつ 878ms」は Redis が遅いのではなく、Python から1回ずつ往復した時間で、PostgreSQL の 968ms もほぼ同じ理由です。SQLite が速いのは同じプロセスの中で動き、往復が無いからです。
- Redis で「値の条件」を引くには、入れるときに索引(ZSET・SET)も一緒に作る必要があります。 作っていなければ SCAN で全件を読むことになり、10万件で 8.9 秒かかりました。
- 「価格の範囲」と「タグ」の両方の条件は、Redis では SET と ZSET の結果をアプリ側で突き合わせました。RDB なら WHERE に AND を書くだけです。
- 大きさは、PostgreSQL の表が索引込みで 10MB、jsonb が 32MB、Redis が 31MB(メモリ)でした。jsonb と Redis は、1件ごとに項目名("name"・"price")を持つためです。
スキーマレスの意味と落とし穴(エラー原文つき)
この節では、「スキーマレス(列を決めなくてよい)」が何を楽にし、何を自分で引き受けることになるのかが分かります。
スキーマレスは「スキーマが無い」のではなく、「スキーマをアプリ側で守る」という意味です。 RDB は決めていない列を拒み、NoSQL(と RDB の JSON 型)は何でも受け取ります。受け取ったあとの型の不揃いは、読む側で気づくことになります。
-- PostgreSQL の jsonb に、揃っていない3件を入れた
INSERT INTO docs VALUES
(1, '{"name":"マウス","price":2980}'),
(2, '{"name":"モニター","price":"34800"}'), -- 価格が文字列
(3, '{"name":"ケーブル","prise":980}'); -- 項目名の綴りが違う
INSERT 0 3 -- 3件とも入る
SELECT id, doc->>'price' AS price, jsonb_typeof(doc->'price') AS type FROM docs ORDER BY id;
id | price | type
----+-------+--------
1 | 2980 | number
2 | 34800 | string
3 | |
SELECT count(*) FROM docs WHERE (doc->'price')::int > 1000;
ERROR: cannot cast jsonb string to type integer
SELECT count(*) FROM docs WHERE (doc->>'price')::int > 1000;
count
-------
2 -- ケーブルは数えられない
ALTER TABLE docs ADD CONSTRAINT price_is_number CHECK (jsonb_typeof(doc->'price') = 'number');
ERROR: check constraint "price_is_number" of relation "docs" is violated by some row
スキーマレスの扱い
手元では、綴り違いの prise が入ったあとでは CHECK 制約を足すこともできなかった
SQLite の表に、決めていない列を入れたときのエラーは OperationalError: table products has no column named color、PostgreSQL は column "color" of relation "products" does not exist でした。RDB のこのエラーは不便に見えますが、綴り違いの prise が入るのを入口で止める働きでもあります。
トランザクションの違い(Redis MULTIとPostgreSQLのROLLBACK)
この節では、「まとめて成功か、まとめて失敗か」を保証する ACID のトランザクションが、RDB と NoSQL でどう違うのかが実行結果で分かります。
PostgreSQL は途中で失敗すると全部を取り消しますが、Redis の MULTI/EXEC は失敗した命令だけが失敗し、残りはそのまま反映されます。 在庫を2つ減らす操作で、2つ目が失敗する状況を作りました。
127.0.0.1:46379> SET stock:1 10
OK
127.0.0.1:46379> SET stock:2 notanumber
OK
127.0.0.1:46379> MULTI
OK
127.0.0.1:46379> DECR stock:1
QUEUED
127.0.0.1:46379> DECR stock:2
QUEUED
127.0.0.1:46379> EXEC
1) (integer) 9
2) (error) ERR value is not an integer or out of range
127.0.0.1:46379> GET stock:1
"9" ← 1つ目は減ったまま。戻らない
t15db=> CREATE TABLE stock (id int PRIMARY KEY, qty int NOT NULL CHECK (qty >= 0));
t15db=> INSERT INTO stock VALUES (1, 10), (2, 0);
t15db=> BEGIN;
t15db=*> UPDATE stock SET qty = qty - 1 WHERE id = 1;
UPDATE 1
t15db=*> UPDATE stock SET qty = qty - 1 WHERE id = 2;
ERROR: new row for relation "stock" violates check constraint "stock_qty_check"
DETAIL: Failing row contains (2, -1).
t15db=!> COMMIT;
ROLLBACK
t15db=> SELECT * FROM stock ORDER BY id;
id | qty
----+-----
1 | 10 ← 1つ目も元に戻っている
2 | 0
Redis の公式ドキュメントは、トランザクションの中で失敗した命令があっても残りは実行され、ロールバックは行わない、と説明しています。これは欠陥ではなく、「速さと単純さのために取り消しの仕組みを持たない」という設計です。1つのキーの中(1つの JSON 文書)だけで完結する更新なら NoSQL でも原子的に行えます。MongoDB の公式ドキュメントも、1つのドキュメントへの操作は原子的で、複数ドキュメントにまたがる原子性が必要なときは分散トランザクション(レプリカセット・シャードクラスタ)に対応する、としています。「複数のデータをまとめて更新し、途中で失敗したら全部戻す」が要るなら、RDB か、その機能を持つ NoSQL を選びます。
CAP定理と結果整合性(レプリカで確かめる)
この節では、NoSQL の説明に必ず出てくる CAP 定理と結果整合性を、Redis のレプリカで実際に起こして確かめた結果で分かります。
CAP 定理は、ネットワークが分断された(P:Partition)ときに、「全台で同じ答えを返す(C:Consistency)」と「どの台も答え続ける(A:Availability)」の両方は守れない、という定理です。 分断は起きるものなので、実際の選択は「分断中に C と A のどちらを優先するか」です。手元で、Redis のマスター(46379)とレプリカ(46380)を立て、レプリカを一時停止して分断を作りました。
master SET hello / replica GET -> hello # 普段は書いた直後に読める
replica 停止中に master SET world # 分断:レプリカに届かない
WAIT 1 300(同じ接続)-> 0 # 300ms 待っても確認済みのレプリカは 0 台
replica GET -> TimeoutError (1秒応答なし)
replica 再開後 WAIT 1 300 -> 1 # 追いついた
replica GET -> world
これが結果整合性(eventual consistency)です。書いた直後はレプリカに無く、「いずれ」揃います。既定の Redis は分断中もマスターへの書き込みを受け続ける(A を優先)設定で、逆に「レプリカが追いついていないなら書き込みを断る」(C を優先)設定もできます。
$ redis-cli -p 46379 CONFIG SET min-replicas-to-write 1
$ redis-cli -p 46379 CONFIG SET min-replicas-max-lag 1
レプリカ正常時 SET -> OK
レプリカ停止 2.5秒後 SET -> NOREPLICAS Not enough good replicas to write.
GET(読むことはできる)-> ok
slave0:ip=127.0.0.1,port=46380,state=online,offset=516,lag=3
レプリカ再開後 SET -> OK
- 1分断中も書き込みを受ける(A 優先)Redis の既定・Cassandra・DynamoDB の結果整合性のある読み込み。後で揃う前提でアプリを作る
- 2揃わないなら書き込みを断る(C 優先)Redis の min-replicas-to-write・MongoDB の w: "majority"・RDB の同期レプリケーション
- 3読む側で選ぶDynamoDB は読み込みごとに「強い整合性」を指定できる(公式)。レプリカから読む設計は RDB でも同じ注意が要る
分断・リトライ・冪等性の考え方は、関連記事「分散システムの落とし穴8選|冪等性・リトライ・CAP定理」で扱っています。
RDBでNoSQL的に使う(PostgreSQLのjsonbとSQLiteのJSON)
この節では、NoSQL を入れずに、RDB の JSON 型で「決めていない項目」を扱う方法と、その限界が分かります。
PostgreSQL の jsonb と SQLite の JSON 関数を使うと、1つの表の中に「決めた列」と「決めていない項目」を同居させられます。 商品の名前と価格は列に、仕様(CPU・インチ・Hz のように商品ごとに違う)は jsonb に入れる、という形です。
-- PostgreSQL 16:jsonb の項目で検索し、GIN 索引を効かせる
CREATE TABLE products_doc (id int PRIMARY KEY, doc jsonb NOT NULL);
CREATE INDEX ix_doc_gin ON products_doc USING gin (doc jsonb_path_ops);
SELECT count(*) FROM products_doc WHERE doc @> '{"tag": "display"}'; -- 12.8 ms(24,922件)
EXPLAIN (COSTS OFF) SELECT count(*) FROM products_doc WHERE doc @> '{"tag": "display"}';
Aggregate
-> Bitmap Heap Scan on products_doc
Recheck Cond: (doc @> '{"tag": "display"}'::jsonb)
-> Bitmap Index Scan on ix_doc_gin
Index Cond: (doc @> '{"tag": "display"}'::jsonb)
-- 数値の範囲は式索引で
CREATE INDEX ix_doc_price ON products_doc (((doc->>'price')::int));
SELECT count(*) FROM products_doc WHERE (doc->>'price')::int BETWEEN 1000 AND 2000; -- 40.6 ms → 1.1 ms
# SQLite 3.45:JSON 関数と ->> 演算子。CHECK で壊れた JSON を拒める
con.execute("CREATE TABLE p2 (id INTEGER PRIMARY KEY, doc TEXT CHECK (json_valid(doc)))")
con.execute("SELECT doc->>'$.spec.inch' FROM p2").fetchone() # (27,)
con.execute("INSERT INTO p2 VALUES (2, '{broken')")
# sqlite3.IntegrityError: CHECK constraint failed: json_valid(doc)
向く
商品ごとに違う仕様・設定・タグなど、列にすると空欄だらけになる項目。
列+jsonb の併用
索引
@> には GIN、数値の範囲には式索引。索引なしの jsonb は表の3倍かかった。
EXPLAIN で確かめる
大きさ
10万件で表 10MB・jsonb 32MB。項目名を毎行持つため。
頻繁に検索する項目は列に
向かない
横に広げる(台数を増やす)前提の大量書き込みや、キャッシュ用途。これは NoSQL の出番。
1台で収まるかが目安
jsonb の演算子と索引の実測は、関連記事「PostgreSQL jsonb操作一覧|演算子早見表とGINインデックス実測」で扱っています。MySQL にも JSON 型があり、同じ考え方で使えます。
NoSQLが向く・向かない(判断表)
この節では、RDB と NoSQL のどちらを選ぶか、選ぶならどの種類かを決める順番が分かります。
迷ったら RDB から始め、「RDB でつらい理由」が具体的に言えるときに NoSQL を足します。 多くのシステムは RDB を中心に、キャッシュやセッションに Redis、ログに列指向型、というように併用しています。
- 1複数のデータをまとめて更新し、失敗したら全部戻す必要があるかある → RDB(会計・在庫・受発注)。手元の Redis MULTI は戻らなかった
- 2引き方が決まっているかキーで1件、キーの範囲、だけなら NoSQL が速い。「後からどんな条件で検索するか分からない」なら RDB
- 3複数の表(データ)を結合・集計するかする → RDB。NoSQL ではアプリ側で突き合わせる(手元の注文×商品)
- 41台に収まらない量・速さか収まらない → 台数を増やす前提の NoSQL(列指向・ドキュメント・DynamoDB)。収まるなら RDB の jsonb で済むことが多い
- 5消えてよいデータかセッション・キャッシュ・ランキングなど、作り直せるもの → Redis。有効期限(EX)が使える
- 6関係を何段もたどるか友達の友達・推薦・経路 → グラフ型。JOIN を重ねる SQL より書きやすい
| 用途 | 向く選択 | 理由 |
|---|---|---|
| 会計・在庫・受発注 | RDB | 複数行の更新を全部戻せる。制約で矛盾を止める |
| ログイン状態・セッション・キャッシュ | キーバリュー(Redis・Valkey) | キーで引く。有効期限で自動で消える |
| 商品カタログ・ユーザープロフィール | ドキュメント型、または RDB+jsonb | 商品ごとに項目が違う。1件を丸ごと読む |
| ログ・IoT・時系列の大量書き込み | 列指向型 | キーで分けて多くの台に書く |
| SNS の関係・推薦・不正検知 | グラフ型 | 関係を何段もたどる |
| サーバーレスで運用の手間を減らしたい | DynamoDB・Firestore | マネージドで台数の管理が要らない |
| 管理画面の自由な検索・集計 | RDB | WHERE・JOIN・GROUP BY をそのまま書ける |
主要なNoSQL製品の一覧(種類・提供元・特徴)
この節では、名前をよく見る NoSQL 製品が、どの種類でどんな形で提供されているのかが分かります。
| 製品 | 種類 | 提供の形 | 特徴 |
|---|---|---|---|
| Redis | キーバリュー(インメモリ) | 自分で立てる/各クラウドのマネージド | 文字列・ハッシュ・リスト・集合・ZSET などのデータ型。有効期限・Pub/Sub |
| Valkey | キーバリュー(インメモリ) | 自分で立てる/AWS・Google Cloud のマネージド | Linux Foundation の下で開発。Redis と同じコマンド体系 |
| Memcached | キーバリュー(インメモリ) | 自分で立てる/マネージド | 文字列のキャッシュに特化。データ型や永続化は持たない |
| Amazon DynamoDB | キーバリュー/ドキュメント | AWS のフルマネージド | パーティションキー+ソートキーで設計。読み込みの整合性を指定できる |
| MongoDB | ドキュメント | 自分で立てる/MongoDB Atlas | BSON 文書。項目で検索・集計。分散トランザクションに対応 |
| Firestore | ドキュメント | Google Cloud のフルマネージド | モバイル・Web からの直接利用とリアルタイム同期 |
| Apache Cassandra | 列指向(ワイドカラム) | 自分で立てる/各社のマネージド | CQL。全台が対等で単一障害点が無い。書き込みに強い |
| Bigtable・HBase | 列指向(ワイドカラム) | Bigtable は Google Cloud、HBase は Hadoop の上 | 行キーの順で並び、範囲読みが速い |
| Neo4j | グラフ | 自分で立てる/Neo4j Aura | Cypher 言語。ノードと関係をパターンで書く |
| Amazon Neptune | グラフ | AWS のフルマネージド | Gremlin・openCypher・SPARQL に対応 |
提供の形や対応する言語は 2026年10月29日時点の各社の公式ドキュメントによります。DynamoDB の使い方は、関連記事「DynamoDBの使い方|AWS CLIとboto3のquery・scan」で扱っています。
よくあるエラー・つまずき(原文つき)
この節では、NoSQL と RDB を使い始めたときに出るエラーと、その意味・直し方が分かります。
| 出たもの(原文) | 意味 | 直し方 |
|---|---|---|
(nil)(Redis の GET) |
キーが無い。期限切れで消えた場合も同じ | EXISTS・TTL で確かめる。キーの綴り(product:2 と product-2)を揃える |
ERR value is not an integer or out of range |
数値でない値に INCR/DECR した | 値を数値で入れ直す。MULTI の中でも他の命令は実行されるので、先に型を確かめる |
READONLY You can't write against a read only replica. |
レプリカに書こうとした | 書き込みはマスターへ。接続先のポートを確かめる |
NOREPLICAS Not enough good replicas to write. |
min-replicas-to-write の条件を満たすレプリカが無い | レプリカの状態(INFO replication の lag)を見る。C 優先の設定が効いている |
table products has no column named color(SQLite) |
決めていない列に入れた | ALTER TABLE … ADD COLUMN するか、JSON の列に入れる |
cannot cast jsonb string to type integer |
jsonb の中の型が揃っていない | ->> で文字列として取り出してから変換する。書き込み側の型を揃える |
CHECK constraint failed: json_valid(doc)(SQLite) |
壊れた JSON を入れた | アプリ側で json.dumps してから入れる。手で書いた JSON の閉じ括弧を見る |
| Redis の全件検索が遅い(手元で 8.9 秒) | 索引にあたるキーを作っていない | 入れるときに SET・ZSET を一緒に作る。KEYS * は本番で使わず SCAN |
引き方を先に書き出す
「id で1件」「ユーザーごとの一覧」「価格の範囲」など、画面や API が必要とする引き方を全部書き出す。NoSQL はこの一覧がそのままキーと索引の設計になる。
まとめて戻す必要がある更新を探す
注文と在庫、送金元と送金先のように、片方だけ成功すると困る更新があれば RDB か、トランザクションを持つ製品にする。
型と項目名を1か所で決める
スキーマレスでも、書き込むコードを1か所にして型を検証する。綴り違いは入ってからでは直しにくい。
レプリカから読む場面を決める
書いた直後に読む画面(登録→確認)はマスターから、一覧やランキングはレプリカから、のように結果整合性でよい場所を決める。
よくある質問
Q. NoSQL とは何ですか? A. 表(テーブル)と SQL を前提にしないデータベースの総称で、「Not only SQL」と読まれます。データの形によってキーバリュー型・ドキュメント型・列指向型・グラフ型の4種類に分かれ、Redis・MongoDB・Cassandra・Neo4j などが代表です。
Q. NoSQL と RDB の違いは何ですか? A. RDB は行と列の表に入れ、SQL で任意の列を検索・結合し、ACID のトランザクションで矛盾を防ぎます。NoSQL は表以外の形で持ち、キーや索引で引くことに特化し、台数を増やして拡張します。手元の実測では、キーで1件引く速さはどちらも速く、索引の無い条件検索と結合で RDB が楽でした。
Q. NoSQL は SQL が使えないという意味ですか? A. 違います。Cassandra の CQL のように SQL に似た言語を持つ製品もあり、PostgreSQL のように SQL の中で JSON を扱える RDB もあります。「SQL だけではない」という意味です。
Q. NoSQL は RDB より速いのですか? A. 得意な引き方では速いですが、どんな操作でも速いわけではありません。手元の10万件では、Redis の索引なしの条件検索は 8.9 秒、SQLite は 4.2 ms でした。キーで引く、索引を作って引く、という使い方で初めて速さが出ます。
Q. スキーマレスとはどういう意味ですか? A. 列と型を先に決めなくてもデータを入れられることです。その代わり、型の不揃いや項目名の綴り違いも入ってしまいます。手元では、価格が文字列の文書と、prise と綴った文書が jsonb に入り、集計がずれました。書き込む側で検証する必要があります。
Q. CAP 定理とは何ですか? A. ネットワークが分断されたとき、「全台で同じ答えを返す(整合性)」と「どの台も答え続ける(可用性)」の両方は守れない、という定理です。手元の Redis では、レプリカを止めても既定ではマスターへ書き込めましたが、min-replicas-to-write を設定すると NOREPLICAS で書き込みが断られました。これが A 優先と C 優先の違いです。
Q. 結果整合性とは何ですか? A. 書いた内容が、すぐではなく「いずれ」全部の台で同じになる、という整合性の弱い保証です。手元では、レプリカを止めている間はマスターに書いた world がレプリカから読めず、再開後に読めるようになりました。
Q. NoSQL にトランザクションはありますか? A. 製品によります。Redis の MULTI/EXEC は命令をまとめて実行しますが、失敗した命令があっても取り消しません(手元で確認)。MongoDB は1つのドキュメントの操作が原子的で、複数ドキュメントにまたがる分散トランザクションにも対応しています(公式ドキュメント)。
Q. RDB の JSON 型を使えば NoSQL は要りませんか? A. 「商品ごとに項目が違う」程度なら、PostgreSQL の jsonb や SQLite の JSON 関数で足りることが多く、GIN 索引や式索引で検索も速くできました。1台に収まらない量や、キャッシュ・セッションのような用途は NoSQL の出番です。
Q. MongoDB と Redis はどちらを選べばよいですか? A. 用途が違います。Redis はメモリ上のキーバリュー型で、キャッシュ・セッション・ランキングのように「キーで引き、消えてもよい」データに向きます。MongoDB はドキュメント型で、項目で検索するアプリの主データに向きます。
Q. 最初に学ぶなら RDB と NoSQL のどちらですか? A. RDB と SQL です。NoSQL の設計は「RDB なら JOIN や WHERE で済むことを、どう肩代わりするか」の判断なので、RDB の考え方が先にあるほうが理解しやすくなります。NoSQL は Redis をキャッシュに使うところから始めると、違いが体で分かります。
まとめ
- NoSQL は「表と SQL」を前提にしないデータベースの総称で、キーバリュー・ドキュメント・列指向・グラフの4種類。データの形と引き方が合っているときに速い。
- 同じ商品データでも、Redis はキーでしか引けず、値の条件は ZSET・SET の索引を自分で作る。10万件の索引なし検索は Redis 8.9 秒・SQLite 4.2 ms だった。
- スキーマレスは「スキーマをアプリ側で守る」こと。jsonb に型の違う価格と綴り違いの項目が入り、集計がずれ、後から CHECK も足せなかった。
- Redis の MULTI/EXEC は失敗しても戻らず、PostgreSQL は全部戻る。レプリカを止めると結果整合性が起き、min-replicas-to-write で C 優先に切り替えられた。
- 迷ったら RDB から。複数データのまとめて更新・自由な検索・結合が要るなら RDB、キーで引く・消えてよい・1台に収まらない・関係をたどるなら NoSQL を足す。
参考・出典
確認日はいずれも 2026年10月29日です。
- Google Cloud「NoSQL データベースとは」:https://cloud.google.com/discover/what-is-nosql?hl=ja
- Oracle「NoSQLとは」:https://www.oracle.com/jp/database/nosql/what-is-nosql/
- Redis「Transactions」:https://redis.io/docs/latest/develop/using-commands/transactions/
- Redis「Replication」(min-replicas-to-write・WAIT):https://redis.io/docs/latest/operate/oss_and_stack/management/replication/
- MongoDB「SQL to MongoDB Mapping Chart」:https://www.mongodb.com/docs/manual/reference/sql-comparison/
- MongoDB「Insert Documents」:https://www.mongodb.com/docs/manual/tutorial/insert-documents/
- MongoDB「db.collection.insertOne()」:https://www.mongodb.com/docs/manual/reference/method/db.collection.insertOne/
- MongoDB「Transactions」:https://www.mongodb.com/docs/manual/core/transactions/
- Apache Cassandra「Data Manipulation (CQL)」:https://cassandra.apache.org/doc/latest/cassandra/developing/cql/dml.html
- Neo4j「MATCH」(Cypher Manual):https://neo4j.com/docs/cypher-manual/current/clauses/match/
- PostgreSQL 16「JSON Types」「JSON Functions and Operators」:https://www.postgresql.org/docs/16/datatype-json.html
- SQLite「JSON Functions And Operators」:https://sqlite.org/json1.html
- AWS「DynamoDB read consistency」:https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.ReadConsistency.html
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「NoSQLとは|RDBとの違い・4種類をRedis/PostgreSQL実測で解説」
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。