Skip to content
Published on

データベース完全比較2025:PostgreSQL vs MySQL vs MongoDB vs Redis — 選定基準と実践ガイド

Authors

はじめに

2025年のデータベースエコシステムは、これまで以上に多様で複雑です。AI/MLワークロードの爆発的な増加によりベクトル検索が必須となり、リアルタイムストリーミング処理、マルチモデル対応、サーバーレスアーキテクチャなどの新しい要件がデータベース選定をさらに困難にしています。

この記事では、PostgreSQL、MySQL、MongoDB、Redisを中心に、DynamoDB、Cassandra/ScyllaDBまで8つの主要データベースのアーキテクチャ、性能、コスト、ユースケースを体系的に比較します。単純な機能の羅列ではなく、実際のプロダクション環境での意思決定に役立つ実践ガイドを提供します。


1. 2025年データベースランキングとトレンド

DB-Enginesランキング(2025年3月時点)

順位データベース種類スコア前年比
1OracleRDBMS1,245-3.2%
2MySQLRDBMS1,142-1.8%
3PostgreSQLRDBMS685+12.4%
4MongoDBDocument397+2.1%
5RedisKey-Value168-5.3%
6ElasticsearchSearch155+1.7%
7SQLiteRDBMS142+8.9%
8CassandraWide-Column112-4.1%

2025年の主要トレンド

1. AIがデータベース選定を変える

RAG(Retrieval-Augmented Generation)パイプラインが普及し、ベクトル検索機能がデータベース選定の核心基準となりました。PostgreSQLのpgvector、MongoDB Atlas Vector Search、Redis Stackがこの競争で頭角を現しています。

2. サーバーレスデータベースの台頭

Neon(PostgreSQLサーバーレス)、PlanetScale(MySQLサーバーレス)、MongoDB Atlas Serverless、DynamoDB on-demandなど、使用量ベースの課金モデルが標準になりつつあります。

3. マルチモデルデータベース

一つのデータベースでリレーショナル、ドキュメント、グラフ、時系列データを全て処理するマルチモデルアプローチが増加しています。PostgreSQLがJSONB、pgvector、PostGISなどの拡張でこの分野をリードしています。

4. Valkeyの登場

Redis Labsのライセンス変更(SSPL)に対応して、Linux FoundationがValkeyフォークをリリースしました。AWS ElastiCache、Google Cloud Memorystoreなどの採用により、Redisエコシステムに大きな変化が起きています。


2. RDBMS vs NoSQL vs NewSQL — いつ何を使うべきか

CAP定理の実践的理解

分散システムでConsistency(一貫性)、Availability(可用性)、Partition Tolerance(分断耐性)の3つを同時に完璧に達成できないという理論です。

         Consistency (C)
            /\
           /  \
          /    \
    CP   / CAP  \ CA
  system/ 不可能 \ system
        /________\
  Partition(P)   Availability(A)
種類組み合わせデータベース特徴
CA一貫性 + 可用性伝統的RDBMS(単一ノード)ネットワーク分断時は利用不可
CP一貫性 + 分断耐性MongoDB, HBase可用性の一部を犠牲
AP可用性 + 分断耐性Cassandra, DynamoDB結果整合性

RDBMSを選ぶべき時

  • トランザクション整合性が必須(金融、決済、在庫管理)
  • 複雑なJOINと集計クエリが頻繁
  • データスキーマが明確で安定的
  • ACID準拠が法的/規制的要件

NoSQLを選ぶべき時

  • スキーマが頻繁に変更される、または非構造化データ
  • 水平スケーリングが核心要件
  • 超低遅延(サブミリ秒)の読み書きが必要
  • 大容量データの高速書き込み処理

NewSQLを選ぶべき時

CockroachDB、TiDB、YugabyteDBなどのNewSQLデータベースは、RDBMSのトランザクション保証とNoSQLの水平スケーラビリティを両方提供します。

  • グローバル分散が必要なトランザクションシステム
  • RDBMS互換性を維持しながら水平スケーリングが必要
  • ただし、成熟度とコミュニティ規模で伝統的RDBMSにはまだ不足

3. PostgreSQLディープダイブ

アーキテクチャ概要

PostgreSQLはプロセスベースのアーキテクチャを使用します。各クライアント接続に専用のバックエンドプロセスが割り当てられます。

┌─────────────────────────────────────────────┐
PostgreSQL│  ┌──────────┐  ┌──────────┐  ┌──────────┐  │
│  │ Backend  │  │ Backend  │  │ Backend  │  │
│  │ Process  │  │ Process  │  │ Process  │  │
│  └────┬─────┘  └────┬─────┘  └────┬─────┘  │
│       │             │             │         │
│  ┌────┴─────────────┴─────────────┴────┐    │
│  │          Shared Memory              │    │
│  │  ┌──────────┐  ┌──────────────┐    │    │
│  │  │ Shared   │  │    WAL       │    │    │
│  │  │ Buffers  │  │  Buffers     │    │    │
│  │  └──────────┘  └──────────────┘    │    │
│  └─────────────────────────────────────┘    │
│                                             │
│  ┌─────────────────────────────────────┐    │
│  │     Background Workers              │    │
│  │  WAL Writer | Checkpointer |        │    │
│  │  Autovacuum | Stats Collector       │    │
│  └─────────────────────────────────────┘    │
└─────────────────────────────────────────────┘

MVCC(Multi-Version Concurrency Control)

PostgreSQLのMVCCは各行の複数バージョンを維持し、読み取り/書き込み間のロック競合を最小化します。

-- トランザクション分離レベル設定
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;

-- MVCC動作確認
BEGIN;
SELECT xmin, xmax, ctid, * FROM orders WHERE id = 1;
-- xmin: 作成トランザクションID, xmax: 削除トランザクションID
-- ctid: 物理的位置 (page, offset)
COMMIT;

JSONB — リレーショナル + ドキュメントの最強の組み合わせ

-- JSONBカラムの作成
CREATE TABLE products (
    id SERIAL PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    metadata JSONB NOT NULL DEFAULT '{}',
    created_at TIMESTAMPTZ DEFAULT NOW()
);

-- GINインデックス作成(JSONB検索最適化)
CREATE INDEX idx_products_metadata ON products USING GIN (metadata);

-- JSONBクエリ例
SELECT name, metadata->>'category' AS category,
       (metadata->'specs'->>'weight')::numeric AS weight
FROM products
WHERE metadata @> '{"category": "electronics"}'
  AND (metadata->'specs'->>'weight')::numeric < 500;

-- JSONB集計
SELECT metadata->>'category' AS category,
       COUNT(*),
       AVG((metadata->'price'->>'amount')::numeric) AS avg_price
FROM products
GROUP BY metadata->>'category';

pgvector — AI/RAG時代のゲームチェンジャー

-- pgvector拡張のインストール
CREATE EXTENSION IF NOT EXISTS vector;

-- ベクトルカラムを持つテーブル
CREATE TABLE documents (
    id SERIAL PRIMARY KEY,
    title TEXT NOT NULL,
    content TEXT NOT NULL,
    embedding vector(1536)  -- OpenAI text-embedding-3-small次元
);

-- HNSWインデックス(高速近似最近傍探索)
CREATE INDEX ON documents
USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 64);

-- IVFFlatインデックス(メモリ効率的)
CREATE INDEX ON documents
USING ivfflat (embedding vector_l2_ops)
WITH (lists = 100);

-- コサイン類似度ベースの検索
SELECT id, title,
       1 - (embedding <=> query_embedding) AS similarity
FROM documents
ORDER BY embedding <=> query_embedding
LIMIT 10;

-- ハイブリッド検索(ベクトル + 全文検索)
SELECT id, title,
       ts_rank(to_tsvector('japanese', content), plainto_tsquery('japanese', 'search')) AS text_score,
       1 - (embedding <=> query_embedding) AS vector_score
FROM documents
WHERE to_tsvector('japanese', content) @@ plainto_tsquery('japanese', 'search')
ORDER BY vector_score * 0.7 + text_score * 0.3 DESC
LIMIT 10;

パーティショニング

-- 範囲パーティショニング例
CREATE TABLE orders (
    id BIGSERIAL,
    order_date DATE NOT NULL,
    customer_id INTEGER NOT NULL,
    total_amount NUMERIC(10,2),
    PRIMARY KEY (id, order_date)
) PARTITION BY RANGE (order_date);

-- パーティション作成
CREATE TABLE orders_2025_q1 PARTITION OF orders
    FOR VALUES FROM ('2025-01-01') TO ('2025-04-01');
CREATE TABLE orders_2025_q2 PARTITION OF orders
    FOR VALUES FROM ('2025-04-01') TO ('2025-07-01');

-- 自動パーティション管理(pg_partman)
CREATE EXTENSION pg_partman;
SELECT partman.create_parent(
    p_parent_table => 'public.orders',
    p_control => 'order_date',
    p_type => 'native',
    p_interval => '3 months'
);

PostgreSQL 17の主要機能

  • Incremental Backup: pg_basebackupでの増分バックアップサポート
  • SQL/JSON標準サポート強化: JSON_TABLE、JSON_QUERY等
  • 性能向上: 並列ハッシュ結合の改善、バルクローディング最適化
  • セキュリティ強化: LDAP/SCRAM認証の改善

4. MySQLディープダイブ

InnoDBアーキテクチャ

┌─────────────────────────────────────────────┐
MySQL Server│  ┌───────────────────────────────────────┐  │
│  │          Connection Pool              │  │
│  │   Thread 1 | Thread 2 | Thread N     │  │
│  └───────────────────────────────────────┘  │
│  ┌───────────────────────────────────────┐  │
│  │    Query Cache(8.0+で廃止)           │  │
│  │          Parser -> Optimizer          │  │
│  └───────────────────────────────────────┘  │
│  ┌───────────────────────────────────────┐  │
│  │            InnoDB Engine              │  │
│  │  ┌──────────┐  ┌──────────────────┐  │  │
│  │  │ Buffer   │  │  Change Buffer   │  │  │
│  │  │ Pool     │  │                  │  │  │
│  │  └──────────┘  └──────────────────┘  │  │
│  │  ┌──────────┐  ┌──────────────────┐  │  │
│  │  │ Redo Log │  │  Undo Log        │  │  │
│  │  └──────────┘  └──────────────────┘  │  │
│  └───────────────────────────────────────┘  │
└─────────────────────────────────────────────┘

MySQLはスレッドベースのアーキテクチャで、PostgreSQLのプロセスベースと対比されます。接続オーバーヘッドが低く、メモリ効率が高いです。

HeatWave ML — MySQLで直接ML

-- HeatWave MLでモデル学習
CALL sys.ML_TRAIN('my_schema.customer_data',
    'churn_label',
    JSON_OBJECT('task', 'classification'),
    @model_handle);

-- 予測実行
CALL sys.ML_PREDICT_ROW(
    JSON_OBJECT('age', 35, 'tenure', 24, 'monthly_charge', 79.99),
    @model_handle,
    @prediction);

SELECT @prediction;

Group Replication(グループレプリケーション)

-- Group Replicationステータス確認
SELECT MEMBER_ID, MEMBER_HOST, MEMBER_PORT, MEMBER_STATE, MEMBER_ROLE
FROM performance_schema.replication_group_members;

-- シングルプライマリモード(デフォルト、推奨)
SET GLOBAL group_replication_single_primary_mode = ON;

ProxySQLで読み書き分離

-- ProxySQLルーティングルール設定
INSERT INTO mysql_query_rules (rule_id, active, match_pattern, destination_hostgroup)
VALUES
    (1, 1, '^SELECT .* FOR UPDATE', 0),   -- 書き込みグループ
    (2, 1, '^SELECT', 1);                  -- 読み取りグループ

-- サーバーグループ設定
INSERT INTO mysql_servers (hostgroup_id, hostname, port, weight)
VALUES
    (0, 'primary.mysql.local', 3306, 1),    -- 書き込み
    (1, 'replica1.mysql.local', 3306, 50),  -- 読み取り
    (1, 'replica2.mysql.local', 3306, 50);  -- 読み取り

5. MongoDBディープダイブ

ドキュメントモデルの強み

// MongoDBドキュメント構造 — 埋め込みパターン
db.orders.insertOne({
  orderId: 'ORD-2025-001',
  customer: {
    name: '田中太郎',
    email: 'tanaka@example.com',
    address: {
      street: '渋谷区神宮前1-2-3',
      city: '東京',
      zipCode: '150-0001',
    },
  },
  items: [
    {
      productId: 'PROD-001',
      name: 'ワイヤレスキーボード',
      price: 8900,
      quantity: 2,
    },
    {
      productId: 'PROD-002',
      name: 'モニタースタンド',
      price: 4500,
      quantity: 1,
    },
  ],
  payment: {
    method: 'card',
    status: 'completed',
    paidAt: ISODate('2025-03-15T10:30:00Z'),
  },
  totalAmount: 22300,
  createdAt: ISODate('2025-03-15T10:25:00Z'),
})
// ベクトル検索インデックス作成
db.collection.createSearchIndex({
  name: 'vector_index',
  definition: {
    mappings: {
      dynamic: true,
      fields: {
        embedding: {
          type: 'knnVector',
          dimensions: 1536,
          similarity: 'cosine',
        },
      },
    },
  },
})

// ベクトル検索クエリ
db.documents.aggregate([
  {
    $vectorSearch: {
      index: 'vector_index',
      path: 'embedding',
      queryVector: queryEmbedding, // 1536次元ベクトル
      numCandidates: 100,
      limit: 10,
      filter: { category: 'tech' },
    },
  },
  {
    $project: {
      title: 1,
      content: 1,
      score: { $meta: 'vectorSearchScore' },
    },
  },
])

MongoDBシャーディング

// シャードキー選択 — カーディナリティ、分散度、クエリパターンを考慮
sh.enableSharding('ecommerce')

// ハッシュシャーディング(均等分散)
sh.shardCollection('ecommerce.orders', { customerId: 'hashed' })

// 範囲シャーディング(範囲クエリ最適化)
sh.shardCollection('ecommerce.logs', { timestamp: 1 })

// ゾーンシャーディング(地域ベースのデータ分離)
sh.addShardTag('shard-jp', 'ASIA')
sh.addShardTag('shard-us', 'AMERICAS')
sh.addTagRange('ecommerce.users', { region: 'JP' }, { region: 'JQ' }, 'ASIA')

6. Redisディープダイブ

データ構造と活用

-- Strings: キャッシュ、セッション、カウンター
SET user:session:abc123 "session_data" EX 3600
INCR page:views:homepage

-- Hashes: オブジェクト格納
HSET user:1001 name "田中太郎" email "tanaka@dev.com" login_count 42
HINCRBY user:1001 login_count 1

-- Sorted Sets: リーダーボード、ランキング
ZADD leaderboard 9500 "player:alice"
ZADD leaderboard 8700 "player:bob"
ZADD leaderboard 9200 "player:charlie"
ZREVRANGE leaderboard 0 9 WITHSCORES  -- Top 10

-- Streams: イベントストリーミング
XADD mystream * sensor_id "temp-01" temperature "23.5" humidity "45"
XREAD COUNT 10 BLOCK 5000 STREAMS mystream 0

-- HyperLogLog: 概算ユニークカウント
PFADD unique:visitors:2025-03-23 "user1" "user2" "user3"
PFCOUNT unique:visitors:2025-03-23

Redis Stack — Search, JSON, TimeSeries

-- Redis JSON
JSON.SET product:001 $ '{"name":"ワイヤレスキーボード","price":8900,"tags":["electronics","keyboard"],"specs":{"weight":350,"wireless":true}}'
JSON.GET product:001 $.specs.weight

-- Redis Searchインデックス作成
FT.CREATE idx:products ON JSON PREFIX 1 product:
  SCHEMA
    $.name AS name TEXT SORTABLE
    $.price AS price NUMERIC SORTABLE
    $.tags[*] AS tags TAG
    $.specs.weight AS weight NUMERIC

-- 全文検索
FT.SEARCH idx:products "@name:keyboard @price:[5000 10000]"

-- ベクトル検索(Redis Stack)
FT.CREATE idx:docs ON HASH PREFIX 1 doc:
  SCHEMA
    title TEXT
    embedding VECTOR HNSW 6 TYPE FLOAT32 DIM 1536 DISTANCE_METRIC COSINE

-- KNN検索
FT.SEARCH idx:docs "*=>[KNN 10 @embedding $query_vec AS score]"
  PARAMS 2 query_vec "\x00\x00..."
  SORTBY score
  RETURN 2 title score

永続化: RDB vs AOF

特性RDB(スナップショット)AOF(Append Only File)
動作方式定期的なメモリダンプ全書き込みコマンドの記録
データ損失最後のスナップショット以降fsyncポリシーに依存
復旧速度高速(バイナリロード)低速(コマンド再実行)
ファイルサイズ小さい(圧縮)大きい(全コマンド記録)
CPU影響fork()時に高いeverysecで低い

Valkeyフォーク — Redisの未来?

項目RedisValkey
ライセンスSSPL(2024~)BSD-3-Clause
クラウドサポートRedis CloudAWS, GCP, Azure
互換性オリジナルRedis 7.2互換
開発主体Redis Ltd.Linux Foundation
コミュニティ制限的な貢献オープン貢献

7. DynamoDB — サーバーレスNoSQLの代表

Single-Table Designパターン

import boto3

dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('EcommerceTable')

# ユーザープロファイル保存
table.put_item(Item={
    'PK': 'USER#001',
    'SK': 'PROFILE',
    'name': '田中太郎',
    'email': 'tanaka@dev.com',
    'type': 'USER'
})

# 同じテーブルに注文を保存
table.put_item(Item={
    'PK': 'USER#001',
    'SK': 'ORDER#2025-03-15#ORD001',
    'items': [{'name': 'キーボード', 'price': 8900}],
    'total': 8900,
    'status': 'completed',
    'type': 'ORDER'
})

# ユーザーの全注文を照会(Query)
response = table.query(
    KeyConditionExpression='PK = :pk AND begins_with(SK, :sk)',
    ExpressionAttributeValues={
        ':pk': 'USER#001',
        ':sk': 'ORDER#'
    }
)

DynamoDB料金モデル

モード読み取りコスト書き込みコスト適合するケース
On-Demand0.25 USD/100万RRU1.25 USD/100万WRUトラフィック予測不可
Provisioned0.00065 USD/RCU/時間0.00065 USD/WCU/時間安定的トラフィック
Reserved最大77%割引最大77%割引1年/3年コミット

8. Cassandra / ScyllaDB — 大規模書き込みの王

Cassandraアーキテクチャ

┌────────┐  ┌────────┐  ┌────────┐
Node A │──│ Node B │──│ Node CToken: │  │ Token: │  │ Token:0-33   │  │ 34-66  │  │ 67-100└───┬────┘  └───┬────┘  └───┬────┘
    │           │           │
    └───────────┼───────────┘
            Gossip Protocol
-- キースペース作成(レプリケーション戦略含む)
CREATE KEYSPACE ecommerce WITH replication = {
    'class': 'NetworkTopologyStrategy',
    'us-east': 3,
    'ap-northeast': 3
};

-- テーブル作成(パーティションキー + クラスタリングキー)
CREATE TABLE ecommerce.user_activity (
    user_id UUID,
    activity_date DATE,
    activity_time TIMESTAMP,
    activity_type TEXT,
    details MAP<TEXT, TEXT>,
    PRIMARY KEY ((user_id, activity_date), activity_time)
) WITH CLUSTERING ORDER BY (activity_time DESC)
  AND compaction = {'class': 'TimeWindowCompactionStrategy',
                    'compaction_window_size': 1,
                    'compaction_window_unit': 'DAYS'};

ScyllaDB — Cassandra互換高性能代替

比較項目CassandraScyllaDB
言語JavaC++(Seastar)
アーキテクチャスレッドベースShard-per-core
GC影響GCポーズありGCなし
同一HW性能基準3-10倍
Cassandra互換オリジナルCQL互換

9. 性能ベンチマーク比較

YCSBベンチマーク(同一HW: 8 vCPU, 32GB RAM, NVMe SSD)

操作PostgreSQLMySQLMongoDBRedisDynamoDBScyllaDB
読取遅延(p99)2.1ms1.8ms1.5ms0.3ms4.2ms0.9ms
書込遅延(p99)3.5ms2.9ms2.1ms0.4ms5.8ms1.2ms
読取TPS45K52K58K250K25K120K
書込TPS28K35K42K200K20K85K
混合(50/50)TPS35K42K48K220K22K100K

注意:ベンチマーク結果はワークロードパターン、データサイズ、インデックス構成等により大きく異なります。


10. ユースケース別選択マトリックス

ユースケース第1選択第2選択理由
ECPostgreSQLMySQLトランザクション + JSONB柔軟性
SNSMongoDBCassandra非構造化データ + 高書き込み
IoTセンサーScyllaDBDynamoDB大容量時系列書き込み
リアルタイム分析PostgreSQLClickHouse複雑な集計クエリ
RAG/AI検索PostgreSQL(pgvector)MongoDB(Atlas Vector)ベクトル + 全文検索
キャッシュRedisDragonflyDB超低遅延、豊富なデータ構造
セッションストアRedisDynamoDB高速読み書き、TTL
チャットMongoDBCassandra柔軟なスキーマ、時間順ソート
ゲームランキングRedisDynamoDBSorted Set、高速更新
ログ管理ElasticsearchMongoDB全文検索、時系列

11. マルチDBアーキテクチャパターン

Polyglot Persistence(多言語永続化)

┌─────────────────────────────────────────────────┐
│                アプリケーション層                  │
└───────┬──────────┬──────────┬──────────┬────────┘
        │          │          │          │
   ┌────▼────┐ ┌──▼───┐ ┌───▼───┐ ┌───▼────┐
   │PostgreSQL│ │Redis │ │MongoDB│ │Elastic │
   (注文/決済)(Cache)(商品)(検索)   └─────────┘ └──────┘ └───────┘ └────────┘

Cache-Asideパターン

import redis
import psycopg2
import json

r = redis.Redis(host='localhost', port=6379)

def get_product(product_id):
    # 1. キャッシュ確認
    cache_key = f"product:{product_id}"
    cached = r.get(cache_key)
    if cached:
        return json.loads(cached)

    # 2. キャッシュミス — DB照会
    conn = psycopg2.connect("dbname=ecommerce")
    cur = conn.cursor()
    cur.execute("SELECT * FROM products WHERE id = %s", (product_id,))
    product = cur.fetchone()

    if product:
        # 3. キャッシュに保存(TTL 1時間)
        r.setex(cache_key, 3600, json.dumps(product))

    return product

12. コスト比較(マネージドサービス)

月間コスト予想(中規模サービス:100GB、5000 TPS)

サービスプロバイダーインスタンス月額コスト(USD)
RDS PostgreSQLAWSdb.r6g.xlarge~350
Cloud SQL PostgreSQLGCPdb-custom-4-16384~320
RDS MySQLAWSdb.r6g.xlarge~350
Atlas MongoDBMongoDBM40~480
ElastiCache RedisAWScache.r6g.xlarge~450
DynamoDB On-DemandAWS-~180-600
Neon PostgreSQLNeonScale~69-189
PlanetScale MySQLPlanetScaleScaler Pro~39-299

13. 面接質問20選

基本概念(1-5)

Q1: ACIDとBASEの違いを説明してください。

ACID(Atomicity, Consistency, Isolation, Durability):伝統的RDBMSのトランザクション属性。全ての操作が完全に成功するか完全に失敗し、データ一貫性を保証します。

BASE(Basically Available, Soft state, Eventually consistent):NoSQLのアプローチ。常に利用可能ですが、即時一貫性の代わりに結果整合性を保証します。

選択基準:金融/決済システムにはACID、SNSフィード/いいねカウンターにはBASEが適合します。

Q2: CAP定理を実務例と共に説明してください。
  • CPシステム(MongoDB):ネットワーク分断発生時、一貫性のために一部ノードの可用性を犠牲。プライマリ選出中は書き込み不可。
  • APシステム(Cassandra, DynamoDB):ネットワーク分断でも全ノードが応答するが、ノード間のデータが一時的に異なる可能性がある。
  • CAシステム:ネットワーク分断がない単一ノードシステム。分散環境では事実上存在しない。
Q3: インデックスの種類と適切な使用例を説明してください。
  • B-Treeインデックス: 範囲検索、ソートに最適。ほとんどのデフォルトインデックス。O(log N)
  • Hashインデックス: 等値比較(=)のみ。O(1)
  • GIN: 配列、JSONB、全文検索に適合
  • GiST: 空間データ(PostGIS)、範囲型に適合
  • BRIN: 物理的にソートされた大規模テーブル(時系列)に効果的
  • Bloomインデックス: 多数のカラムに対する等値比較フィルタリング
Q4: N+1クエリ問題とは何で、どう解決しますか?

N+1問題:1回のクエリでN件の結果を取得した後、各結果に対して追加クエリをN回実行する非効率的なパターン。

解決方法:

  • JOIN: リレーショナルDBで1回のクエリでデータを結合
  • Eager Loading: ORMで関連データを事前ロード
  • DataLoaderパターン: GraphQLでバッチリクエストにまとめて処理
  • MongoDB: 埋め込みパターンで関連データを1つのドキュメントに格納
Q5: データベース正規化と非正規化のトレードオフを説明してください。

正規化(1NF-BCNF):

  • メリット:データ重複最小化、一貫性維持が容易、ストレージ節約
  • デメリット:JOIN増加で読み取り性能低下、複雑なクエリ

非正規化:

  • メリット:読み取り性能向上(JOIN減少)、単純なクエリ
  • デメリット:データ重複、更新異常リスク、ストレージ増加

実務ガイド:OLTPは3NF以上の正規化、OLAP/読み取り集中ワークロードは意図的な非正規化を検討。

深化問題(6-10)

Q6: PostgreSQLのMVCCとMySQL InnoDBのMVCCの違いは?

PostgreSQL MVCC

  • 各行の複数バージョンをテーブルに直接格納(xmin, xmax)
  • 古いバージョンはVACUUMで整理
  • メリット:読み取り時ロックなし、高い同時実行性
  • デメリット:テーブル膨張(bloat)、VACUUMオーバーヘッド

MySQL InnoDB MVCC

  • Undo Logに以前のバージョンを格納
  • 現在の行には常に最新バージョン
  • Purgeスレッドが不要なUndoを整理
  • メリット:テーブル膨張なし、シンプルな管理
  • デメリット:Undoログ増大時の性能低下
Q7: シャーディング戦略の種類と選択基準は?
  1. ハッシュシャーディング: キーをハッシュして均等分散。範囲クエリが非効率的。
  2. 範囲シャーディング: キー範囲で分割。ホットスポットリスク。時系列に適合。
  3. 地理ベースシャーディング: ユーザー位置ベースの分割。地域別規制準拠に有用。
  4. ディレクトリベースシャーディング: マッピングテーブルで柔軟なルーティング。管理複雑度が高い。

シャードキー選択基準:高いカーディナリティ、均等分散、クエリパターンと一致。

Q8: Redisはシングルスレッドなのになぜ速いのですか?
  1. インメモリ処理: 全データがRAMにあるためディスクI/Oなし
  2. I/O多重化: epoll/kqueueで数千接続を単一スレッドで処理
  3. ロック不要: シングルスレッドのため同時実行制御オーバーヘッドなし
  4. 最適化されたデータ構造: SDS、ziplist、intset等のメモリ効率的な構造
  5. Redis 6.0+: I/Oスレッディング導入(コマンド処理は依然としてシングルスレッド)
Q9: MongoDBで埋め込みとリファレンスのどちらを選ぶべきですか?

埋め込み(サブドキュメント):

  • 一緒に照会されるデータ
  • 1:1または1:少数の関係
  • 16MBドキュメントサイズ制限内
  • 例:注文と注文アイテム、ユーザーと住所

リファレンス(別コレクション):

  • 独立して照会/更新されるデータ
  • 1:多数または多:多の関係
  • ドキュメントサイズが急速に増加する場合
  • 例:ブログ記事とコメント、商品とレビュー
Q10: コネクションプーリングが重要な理由と設定方法は?

DB接続作成コスト:TCPハンドシェイク + 認証 + セッション割り当てで数~数十ms必要。リクエスト毎に接続を作成すると性能が大きく低下します。

設定例(HikariCP):

  • maximumPoolSize: 一般的にCPUコア数 x 2 + ディスク数
  • minimumIdle: 最小アイドル接続数
  • connectionTimeout: プールから接続を待つ最大時間
  • idleTimeout: アイドル接続維持時間

PostgreSQLはプロセスベースなので、PgBouncerを前に置いてコネクションプーリングを管理するのがベストプラクティスです。

実践/システム設計(11-15)

Q11: 大規模トラフィックECサービスのDBアーキテクチャを設計してください。
  1. 商品カタログ: MongoDB(柔軟な属性、高速読み取り)
  2. 注文/決済: PostgreSQL(ACIDトランザクション)
  3. カート/セッション: Redis(TTL、高速読み書き)
  4. 商品検索: Elasticsearch(全文検索、ファセット)
  5. レコメンドシステム: PostgreSQL pgvector(ベクトル類似度)
  6. 分析/レポート: ClickHouseまたはBigQuery
Q12: データベースマイグレーション戦略を説明してください。
  1. Dual-Writeパターン: 両方のDBに同時書き込み、段階的に読み取りを切り替え
  2. CDC(Change Data Capture): Debezium等でリアルタイムデータ同期
  3. Shadow Traffic: 新DBにトラフィックをミラーリングして性能検証
  4. Blue-Green: 2つの環境を準備して一度に切り替え
Q13: レプリケーション遅延問題をどう解決しますか?
  1. Read-your-writes一貫性: 書き込み後、短時間はマスターから読み取り
  2. 最小レプリケーション確認: PostgreSQLのsynchronous_commit設定
  3. 遅延モニタリング: pg_stat_replicationreplay_lagチェック
  4. GTIDベースルーティング: MySQL GTIDで特定時点まで反映されたレプリカにルーティング
  5. キャッシュ活用: 書き込み直後にキャッシュに最新値を保存
Q14: 大規模テーブルでスキーマ変更を安全に実行するには?

PostgreSQL:

  • ALTER TABLE ADD COLUMN(DEFAULT無し):即時完了
  • CREATE INDEX CONCURRENTLY:テーブルロックなしでインデックス作成

MySQL:

  • pt-online-schema-change(Percona):トリガーベースのオンライン変更
  • gh-ost(GitHub):バイナリログベースのオンライン変更
  • MySQL 8.0+ INSTANT DDL:メタデータのみの変更操作

MongoDB:

  • スキーマレスなのでほとんどのマイグレーション不要
Q15: DB性能モニタリングで最も重要な指標は?
  1. クエリ性能: スロークエリログ、p95/p99レスポンスタイム
  2. 接続数: アクティブ/アイドル接続、コネクションプール使用率
  3. キャッシュヒット率: Buffer Pool/Shared Bufferヒット率(目標:99%以上)
  4. レプリケーション遅延: マスター/レプリカ間の遅延時間
  5. ディスクI/O: IOPS、レイテンシ、使用率
  6. ロック待機: 行ロック/テーブルロック待機時間と頻度
  7. トランザクションスループット: TPS、コミット/ロールバック比率

深化実践問題(16-20)

Q16: Hot Partition問題とは何で、どう解決しますか?

特定のパーティション/シャードにトラフィックが集中し、性能ボトルネックが発生する問題です。

解決方法:

  1. Write Sharding: キーにランダムサフィックス追加
  2. Composite Key: 日付/時間等をキーに含めて分散
  3. キャッシュレイヤー: 読み取りホットスポットをRedisで吸収
  4. 分散カウンター: 1つのカウンターをN個に分割して合算
Q17: バックアップ戦略の種類とRPO/RTOを説明してください。
  • RPO(Recovery Point Objective): 許容可能な最大データ損失時間
  • RTO(Recovery Time Objective): 許容可能な最大復旧時間

バックアップ種類:

  1. フルバックアップ: 全データ。RTO短い、ストレージ大きい
  2. 増分バックアップ: 前回以降の変更分。ストレージ少ない、復旧複雑
  3. 継続的アーカイビング(WAL): PITR可能。RPOほぼゼロ
  4. スナップショット: EBSスナップショット、RDS自動バックアップ
Q18: スロークエリの分析と最適化プロセスは?
  1. 識別: スロークエリログ有効化
  2. 分析: EXPLAIN ANALYZEで実行計画確認
  3. 最適化順序:
    • インデックス追加/改善
    • クエリリファクタリング
    • パーティショニング適用
    • データアーカイビング
    • 非正規化検討
  4. 検証: 変更前後の性能比較
  5. モニタリング: 継続的な観察
Q19: Eventually Consistentシステムでデータ整合性をどう保証しますか?
  1. Read Repair: 読み取り時に不整合を発見したら最新値で修正
  2. Anti-Entropy(Merkle Tree): バックグラウンドでノード間データ比較/修正
  3. Conflict Resolution: LWW、ベクタークロック、カスタムマージロジック
  4. Quorum読み書き: R + W > N条件で強い一貫性確保
  5. Sagaパターン: 分散トランザクションの代わりに補償トランザクションで一貫性維持
Q20: 2025年のDB選定で最も重要な要素3つは?
  1. AI/ベクトル検索サポート: RAGパイプラインが標準になり、ベクトル検索機能が必須。pgvector、Atlas Vector Search、Redis Vector Searchから選択。

  2. 運用複雑度 vs コスト: マネージドサービスの利便性 vs セルフホスティングのコスト削減。チーム規模とDevOps能力に基づいて決定。

  3. データ局所性と規制: GDPR、各国の個人情報保護法等のデータ主権規制。DynamoDB Global Tables、CockroachDBの地域ベース分散が解決策になりうる。


14. クイズ

クイズ1: PostgreSQLでJSONBとJSON型の主な違い2つは?
  1. 格納方式: JSONはテキストのまま格納、JSONBはバイナリに分解して格納。JSONBは書き込みが若干遅いが、読み取り/検索が遥かに速い。
  2. インデックス: JSONBはGINインデックスをサポートし、キー/値検索がO(log N)。JSONはインデックス不可能。
クイズ2: RedisのSorted Setが内部で使用するデータ構造は?

Skip List + Hash Tableの組み合わせです。

  • Skip List: ソートされた順序で範囲検索 O(log N)
  • Hash Table: スコア参照 O(1)

要素が128個以下で値が64バイト以下の場合、ziplist(7.0+ではlistpack)を使用してメモリを節約します。

クイズ3: DynamoDBでGSIとLSIの違いは?
項目GSILSI
パーティションキーテーブルと異なる可能テーブルと同一
ソートキー自由に選択自由に選択
容量制限なしパーティション当たり10GB
作成タイミングテーブル作成後も可能テーブル作成時のみ
一貫性結果整合性のみ強い一貫性も可能

実務ヒント:ほとんどの場合GSIが柔軟なので、GSIを優先使用します。

クイズ4: MongoDBでトランザクションを使うべき時と避けるべき時は?

使うべき時:

  • 複数ドキュメント/コレクションにまたがる原子的更新
  • データ一貫性がビジネス的に必須

避けるべき時:

  • 単一ドキュメント更新(MongoDBは単一ドキュメントレベルで既に原子的)
  • 高スループットが必要な操作
  • シャード環境での頻繁なマルチシャードトランザクション
クイズ5: VACUUMがPostgreSQLで重要な理由は?

PostgreSQLのMVCCは更新/削除時に以前のバージョンを即座に削除しません。VACUUMはこの「デッドタプル」を整理します。

行わないと:

  1. テーブル膨張: ディスク空間の浪費、シーケンシャルスキャン性能低下
  2. インデックス膨張: インデックスサイズ増加、検索性能低下
  3. トランザクションIDラップアラウンド: 約20億トランザクション後にデータ損失リスク

Autovacuumがデフォルトで有効ですが、書き込み負荷の高いテーブルはパラメータチューニングが必要です。


参考資料


この記事で取り上げた8つのデータベースは、それぞれ固有の強みを持っています。「最高のデータベース」は存在せず、「最適なデータベース」のみが存在します。ワークロードパターン、スケーリング要件、チーム能力、コスト制約を総合的に考慮して選択してください。そして1つのデータベースに全てを任せようとせず、Polyglot Persistence戦略で各データベースの強みを組み合わせることが2025年のベストプラクティスです。