メインコンテンツへ移動
SEMentor
データベース 約10分

NoSQL基礎 — MongoDB・Redis・DynamoDB

RDBとの違いを理解し、ドキュメントDB・KVS・ワイドカラムの使い分けを学ぶ

NoSQLとは

**NoSQL(Not Only SQL)**は、RDB の制約を超えるために生まれたデータベースの総称です。「SQL を使わない」ではなく「SQL だけに縛られない」という意味です。

2000年代後半、Google・Amazon・Facebook が大規模データ処理の限界からそれぞれ独自のDBを開発し、その知見がオープンソースに還元されました。

RDB vs NoSQL

特性RDBNoSQL
スキーマ固定(テーブル定義が必要)柔軟(スキーマレスが多い)
スケール垂直スケール(サーバーを強化)水平スケール(サーバーを追加)
トランザクションACID保証BASE(結果整合性が多い)
関係表現JOIN で多対多を表現埋め込みや非正規化で対応
向いている用途複雑なクエリ・整合性重視大量データ・高スループット・柔軟スキーマ

NoSQLの分類

種類代表製品データモデル
ドキュメントDBMongoDB, FirestoreJSONライクな文書
キーバリューDBRedis, DynamoDBキーと値のペア
ワイドカラムDBCassandra, HBase行+動的カラム
グラフDBNeo4j, 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を組み合わせて使う

このレッスンは未完了です。

次のレッスンへ