SQL vs NoSQL:あなたのプロジェクトにはどちらを選ぶべきか

Database tutorial - IT technology blog
Database tutorial - IT technology blog

3日間を費やした失敗から始まる話

2年前、100GBのデータベースをMySQLからPostgreSQLへマイグレーションした経験がある — プランニングだけで3日、実行に1日かかった。システムが複雑だったからではなく、最初にチームが間違ったツールを選んでしまったからだ。MySQLは複雑なJOINや厳密なトランザクションが必要なプロジェクトに使われていたが、スキーマはNoSQL的な設計 — 柔軟で制約なし — になっていた。結果:クエリは遅く、パフォーマンスは悪く、リファクタリングに何週間も費やすことになった。

初歩的なミスに聞こえるかもしれない。しかしこれは、多くのチームがプロジェクト開始からわずか数分で下すアーキテクチャの決断であり、その後何ヶ月も代償を払い続けることになるのだ。

現実の問題:なぜデータベースの選択ミスがそれほど痛いのか

こんな状況を想像してほしい:ECサイトを構築する際に「NoSQLは速くて柔軟だ」という理由でMongoDBを選んだとする。売上レポートが必要になった時 — ordersとusersをJOIN、カテゴリ別に集計、日付範囲でフィルタリング — aggregationパイプラインを何ページにも渡って書かなければならない。SQLなら3行で返せる数値を出すのに、デバッグだけで半日かかってしまう。

逆の場合も同様だ。数千台のデバイスから毎秒50,000イベントを書き込む必要のあるIoTアプリケーションにMySQLを使うと:スキーマは硬直的、書き込みスループットは低く、数週間も経てばシステムはパンクし始める。

どちらの根本原因も同じだ:データの特性や実際のアクセスパターンではなく、「トレンド」や「使い慣れ」でデータベースを選んでしまうこと。

原因分析:SQLとNoSQLはそれぞれ何のために設計されたのか

SQL — データの整合性を保証するために生まれた

リレーショナルデータベース — PostgreSQL、MySQL、SQLite、SQL Server — は一つの原則を中心に構築されている:データは矛盾した状態にあってはならない。それを保証するために、4つのコアな特性を持っている:

  • 固定スキーマ:テーブル内のすべての行が同じカラムを持ち、正確なデータ型に従う — 例外は許されない
  • ACIDトランザクション:Atomicity(原子性)、Consistency(一貫性)、Isolation(分離性)、Durability(永続性) — データが「中途半端」な状態になることはない
  • リレーション:外部キー、JOIN — データを厳密かつ一貫した形でリンクする
  • 垂直スケーリング:サーバーをアップグレード(RAM、CPU、SSD)することで拡張する
-- 典型的なSQLクエリ:注文情報と顧客情報を取得する
SELECT 
    o.id          AS order_id,
    u.name        AS customer_name,
    o.total_amount,
    o.created_at
FROM orders o
JOIN users u ON o.user_id = u.id
WHERE o.status = 'completed'
  AND o.created_at >= '2025-01-01'
ORDER BY o.created_at DESC;

NoSQL — 柔軟なスケーリングのために生まれた

NoSQLは一つの技術ではなく、共通の特性を持つデータベース群のことだ:リレーショナルモデルを使わず、固定スキーマよりもスケールアウトを優先する。主要な4つの種類があり、それぞれがまったく異なる問題を解決する:

  • ドキュメントストア(MongoDB、CouchDB):JSON/BSONドキュメントを保存 — 半構造化データに適している
  • キーバリューストアRedis、DynamoDB):キーによる超高速アクセス — キャッシュやセッション管理に適している
  • カラムファミリー(Cassandra、HBase):超高い書き込みスループット — 時系列データやIoTに適している
  • グラフデータベース(Neo4j):エンティティ間の複雑な関係 — ソーシャルネットワークや推薦エンジンに適している
// MongoDBドキュメント — 柔軟なユーザープロファイル
{
  "_id": "usr_001",
  "name": "山田太郎",
  "email": "[email protected]",
  "preferences": {
    "theme": "dark",
    "language": "vi",
    "notifications": ["email", "push"]
  },
  "social_accounts": [
    { "platform": "github", "username": "vana_dev" },
    { "platform": "linkedin", "url": "linkedin.com/in/vana" }
  ]
}

柔軟なスキーマ — 新しいフィールドを追加するときにALTER TABLEは不要だ。ユーザーAがsocial_accountsを持っていても、ユーザーBが持っていなくても — MongoDBはそれを気にしない。MySQLでは、マイグレーションを実行し、既存のすべての行のNULL値を処理しなければならない。

解決策:それぞれのユースケースに合った選択をする

SQLを使うべき時

以下のケースでNoSQLを選ぶと、非常に苦労することになる:

  • 金融・会計:すべてのトランザクションはACIDを保証しなければならない — 「途中」でお金が消えることは許されない
  • Eコマース:注文、在庫、決済 — 複雑なリレーション、頻繁なJOINが必要
  • ERP/CRM:企業データは安定したスキーマを持ち、時間とともにほとんど変化しない
  • 複雑なレポートGROUP BYHAVING、サブクエリ、ウィンドウ関数
-- ウィンドウ関数:月別売上ランキングを計算する
SELECT 
    product_name,
    month,
    revenue,
    RANK() OVER (
        PARTITION BY month 
        ORDER BY revenue DESC
    ) AS revenue_rank
FROM monthly_sales
WHERE year = 2025;

NoSQLを使うべき時

逆に、以下のケースでSQLを無理に使うのは非常に窮屈だ:

  • リアルタイムアプリケーション:チャット、ゲーミング — Redisのpub/subまたはMongoDBのchange streams
  • コンテンツ管理:ブログ、商品カタログ — スキーマがビジネス要件に合わせて頻繁に変化する
  • IoT / 時系列データ:毎日数百万のイベント — CassandraまたはInfluxDBの方がはるかに優れた処理ができる
  • セッション、キャッシュ:TTL付きのRedisが期限切れセッションを自動削除するため、クリーンアップジョブが不要
# Redis:セッションをTTL 30分で保存する
redis-cli SET "session:usr_001" '{"user_id":1,"role":"admin"}' EX 1800

# セッションを読み取る
redis-cli GET "session:usr_001"

# 残り時間を確認する(秒)
redis-cli TTL "session:usr_001"
# Python:IoTデータをCassandraに書き込む — 超高い書き込みスループット
from cassandra.cluster import Cluster
from datetime import datetime

cluster = Cluster(['cassandra-node1', 'cassandra-node2'])
session = cluster.connect('iot_keyspace')

insert_query = """
    INSERT INTO sensor_readings (device_id, timestamp, temperature, humidity)
    VALUES (%s, %s, %s, %s)
"""

session.execute(insert_query, ('sensor_001', datetime.now(), 28.5, 65.2))
print("書き込み成功")

ベストな方法:それぞれの役割に応じて両方を使う(Polyglot Persistence)

十分に複雑なプロダクションアプリケーションで、1種類のデータベースだけで済むものはない。NetflixはCassandraを視聴履歴に、MySQLを課金処理に、Redisをセッション管理に使っている。UberはPostgreSQLをトリップデータに、Redisをリアルタイムマッチングに使っている。これがPolyglot Persistence — 各データベースが自分の役割をこなし、一つですべてをやろうとしない。

本格的なECアプリケーションのアーキテクチャ例:

  • PostgreSQL:注文、ユーザー、在庫 — 厳密なACIDとJOINが必要
  • Redis:セッション、一時的なカート、レート制限、商品詳細キャッシュ
  • MongoDB:商品カタログ — カテゴリごとに異なる属性を持ち、柔軟なスキーマが必要
  • Elasticsearch:商品の全文検索、多次元フィルタリング

素早く決断するために、私はいつも3つの質問を自分に問いかける:

  1. データは固定された構造を持っているか? — Yesなら → SQL
  2. JOINやマルチテーブルトランザクションが必要か? — Yesなら → SQL
  3. 高い書き込みスループットや水平スケールが必要か? — Yesなら → NoSQL

3つすべて確信が持てない?PostgreSQLから始めよう。JSONBで柔軟な部分をサポートし、全文検索も内蔵されており、中規模のほとんどのワークロードを処理できる。後でさらにスケールが必要になった時、スキーマ全体をリファクタリングするよりも、段階的にNoSQLへマイグレーションする方がはるかに簡単だ。

これが100GBマイグレーションから学んだ教訓だ:最初からPostgreSQLを使い、柔軟な部分にはJSONBを、制約が必要な部分には標準スキーマを使っていれば、マイグレーションは一切不要で — 少なくとも4人日を節約できたはずだ。

Share: