NoSQLとは
**NoSQL(Not Only SQL)**は、RDB の制約を超えるために生まれたデータベースの総称です。「SQL を使わない」ではなく「SQL だけに縛られない」という意味です。
2000年代後半、Google・Amazon・Facebook が大規模データ処理の限界からそれぞれ独自のDBを開発し、その知見がオープンソースに還元されました。
RDB vs NoSQL
| 特性 | RDB | NoSQL |
|---|---|---|
| スキーマ | 固定(テーブル定義が必要) | 柔軟(スキーマレスが多い) |
| スケール | 垂直スケール(サーバーを強化) | 水平スケール(サーバーを追加) |
| トランザクション | ACID保証 | BASE(結果整合性が多い) |
| 関係表現 | JOIN で多対多を表現 | 埋め込みや非正規化で対応 |
| 向いている用途 | 複雑なクエリ・整合性重視 | 大量データ・高スループット・柔軟スキーマ |
NoSQLの分類
| 種類 | 代表製品 | データモデル |
|---|---|---|
| ドキュメントDB | MongoDB, Firestore | JSONライクな文書 |
| キーバリューDB | Redis, DynamoDB | キーと値のペア |
| ワイドカラムDB | Cassandra, HBase | 行+動的カラム |
| グラフDB | Neo4j, Amazon Neptune | ノードとエッジ |
MongoDB — ドキュメントDB
JSON(実体はBSON)でデータを保存します。スキーマを事前に定義しなくてよいため、フレキシブルな構造のデータに適しています。
// RDBの users テーブル + orders テーブルを
// MongoDBでは1つのドキュメントに埋め込める
// RDB(正規化)
// users: { id, name, email }
// orders: { id, user_id, product, amount }
// MongoDB(非正規化・埋め込み)
{
"_id": ObjectId("..."),
"name": "田中太郎",
"email": "tanaka@example.com",
"orders": [
{ "product": "書籍A", "amount": 2800, "date": "2026-04-20" },
{ "product": "書籍B", "amount": 3200, "date": "2026-04-25" }
],
"address": {
"prefecture": "東京都",
"city": "渋谷区"
}
}
// MongoDB 基本操作(JavaScript / mongosh)
// 挿入
db.users.insertOne({ name: "山田花子", email: "yamada@example.com", age: 28 })
db.users.insertMany([{ name: "鈴木一郎" }, { name: "佐藤次郎" }])
// 検索
db.users.find({ age: { $gte: 25 } }) // age >= 25
db.users.find({ name: /^田中/ }) // 正規表現
db.users.findOne({ email: "tanaka@example.com" })
db.users.find({}).sort({ age: -1 }).limit(10) // 降順・10件
// 更新
db.users.updateOne({ _id: id }, { $set: { age: 29 } })
db.users.updateMany({ age: { $lt: 20 } }, { $set: { category: "young" } })
// 削除
db.users.deleteOne({ _id: id })
// インデックス(検索高速化)
db.users.createIndex({ email: 1 }, { unique: true })
db.users.createIndex({ age: 1, name: 1 }) // 複合インデックス
MongoDB が向いているケース:
- ECサイトの商品カタログ(属性が商品ごとに異なる)
- コンテンツ管理(記事、ブログ)
- ユーザープロファイル(フレキシブルな属性)
Redis — キーバリューDB
超高速なインメモリDB。主にキャッシュ・セッション管理・リアルタイム機能に使われます。
import redis
r = redis.Redis(host='localhost', port=6379, db=0)
# --- 文字列 ---
r.set('user:1:name', '田中太郎')
r.get('user:1:name') # b'田中太郎'
r.setex('session:abc', 3600, 'user_123') # 1時間後に自動削除(セッション管理)
# --- ハッシュ(オブジェクト) ---
r.hset('user:1', mapping={'name': '田中太郎', 'age': '30'})
r.hget('user:1', 'name') # b'田中太郎'
r.hgetall('user:1') # 全フィールド
# --- リスト(キュー) ---
r.rpush('queue:jobs', 'job1', 'job2', 'job3') # 末尾に追加
r.lpop('queue:jobs') # 先頭から取得(FIFO)
r.lrange('queue:jobs', 0, -1) # 全件取得
# --- セット(重複なし) ---
r.sadd('online_users', 'user1', 'user2', 'user3')
r.smembers('online_users') # 全メンバー
r.sismember('online_users', 'user1') # メンバーか確認
# --- ソート済みセット(スコア付き) ---
r.zadd('ranking', {'user1': 100, 'user2': 85, 'user3': 92})
r.zrange('ranking', 0, -1, withscores=True) # 昇順
r.zrevrange('ranking', 0, 2, withscores=True) # 上位3件(降順)
# --- TTL確認 ---
r.ttl('session:abc') # 残り秒数
r.persist('key') # TTLを削除(永続化)
Redis が向いているケース:
- セッション管理(
SETEXで有効期限付き) - キャッシュ(DBクエリ結果をRedisに保存してDBへのアクセスを削減)
- ランキング(Sorted Set)
- リアルタイムカウンター(ページビュー、いいね数)
- ジョブキュー(List / Stream)
- PubSub(リアルタイム通知)
# キャッシュの典型的な実装パターン
import json
def get_user_cached(user_id: int):
cache_key = f"user:{user_id}"
cached = r.get(cache_key)
if cached:
return json.loads(cached) # キャッシュヒット
user = db.users.find_one(user_id) # DBから取得
r.setex(cache_key, 300, json.dumps(user)) # 5分キャッシュ
return user
DynamoDB — マネージドKVS
AWS のフルマネージドNoSQLです。無制限のスケール・ミリ秒以下のレスポンスが特徴。
# boto3 (AWS SDK) での操作
import boto3
dynamodb = boto3.resource('dynamodb', region_name='ap-northeast-1')
table = dynamodb.Table('Users')
# 挿入(PK: user_id, SK: profile)
table.put_item(Item={
'user_id': 'user_123', # Partition Key
'sk': 'profile', # Sort Key
'name': '田中太郎',
'email': 'tanaka@example.com',
'created_at': '2026-04-25T10:00:00Z',
})
# 取得
response = table.get_item(Key={'user_id': 'user_123', 'sk': 'profile'})
user = response.get('Item')
# クエリ(Partition Key が一致する全アイテム)
from boto3.dynamodb.conditions import Key
response = table.query(
KeyConditionExpression=Key('user_id').eq('user_123')
)
# 更新
table.update_item(
Key={'user_id': 'user_123', 'sk': 'profile'},
UpdateExpression='SET #n = :name',
ExpressionAttributeNames={'#n': 'name'},
ExpressionAttributeValues={':name': '田中次郎'},
)
アクセスパターン設計の重要性: DynamoDBは事前にアクセスパターンを定義してテーブル設計する必要があります。RDBのように後から自由にJOINできません。「どのクエリが必要か」を先に整理することが設計の核心です。
使い分け早見表
要件 推奨
─────────────────────────────────────────────────
複雑なJOIN・集計が必要 RDB(PostgreSQL)
スキーマが固定・整合性が最重要 RDB(PostgreSQL)
JSONライクな柔軟な構造 MongoDB
高速キャッシュ・セッション管理 Redis
リアルタイムランキング・カウンター Redis
AWSでフルマネージド・無制限スケール DynamoDB
大規模時系列データ(IoT・ログ) InfluxDB / Cassandra
SNSのような複雑なグラフ関係 Neo4j
まとめ
- NoSQLは「RDBの代替」ではなく「RDBが苦手なユースケースへの解」
- MongoDB: 柔軟なスキーマ・ドキュメント指向
- Redis: インメモリ・超高速・キャッシュ/セッション
- DynamoDB: フルマネージド・水平スケール・AWSネイティブ
- 多くの本番システムはRDBとNoSQLを組み合わせて使う