データベースのスケーリング戦略: リードレプリカ、シャーディングなど
Web アプリケーションのスケーリング問題の 78% において、データベースのパフォーマンスがボトルネックとなっています。 アプリケーションは最小限の労力で水平方向にスケーリングできますが、データベースは水平方向のスケーリングに抵抗します。データベースのスケーリングのために選択した戦略によって、アプリケーションが許容可能なパフォーマンスで 100 ユーザーにサービスを提供するか、100,000 ユーザーにサービスを提供できるかが決まります。
このガイドでは、スケーリングの必要性を遅らせる単純な最適化から、水平シャーディングなどの高度な技術に至るまで、データベースのスケーリング戦略の全範囲をカバーしています。
重要なポイント
- インフラストラクチャを追加する前にクエリを最適化し、インデックスを追加します --- これにより、データベースのパフォーマンスの問題の 60% が解決されます
- リードレプリカは最もリスクの低いスケーリング戦略であり、読み取り負荷の高いワークロードの 80% を処理します
- アプリケーションが 10 を超えるインスタンスを実行する場合、接続プーリングは必須です
- 水平シャーディングは、アプリケーションが大幅に複雑になる最後の手段です
スケーリングラダー
この順序でスケールします。各ステップは次のステップよりも安価でリスクが低くなります。
ステップ 1: クエリの最適化 (無料)
インフラストラクチャを追加する前に、既存のデータベースが最適に実行されていることを確認してください。
-- Find slow queries in PostgreSQL
SELECT
calls,
mean_exec_time::numeric(10,2) AS avg_ms,
total_exec_time::numeric(10,2) AS total_ms,
query
FROM pg_stat_statements
ORDER BY mean_exec_time DESC
LIMIT 20;
一般的な最適化:
- 頻繁にフィルターされる列に欠落しているインデックスを追加する
SELECT *を特定の列リストに置き換えますEXPLAIN ANALYZEを使用して、大きなテーブルの順次スキャンを識別します- 複数列の WHERE 句に複合インデックスを追加する
OFFSETの代わりにキーセット ページネーションを使用してページネーションを実装します。
-- Bad: OFFSET pagination (scans all skipped rows)
SELECT * FROM orders ORDER BY created_at DESC LIMIT 20 OFFSET 10000;
-- Good: Keyset pagination (index-only scan)
SELECT * FROM orders
WHERE created_at < '2026-03-15T10:00:00Z'
ORDER BY created_at DESC
LIMIT 20;
ステップ 2: 垂直スケーリング ($)
既存のデータベース サーバーの CPU、RAM、ストレージを増設します。これにより時間を稼ぐことができ、アプリケーションの変更は必要ありません。
| インスタンスのサイズ | vCPU | RAM | 接続 | 月額費用 (RDS) |
|---|---|---|---|---|
| db.t3.medium | 2 | 4GB | 100 | $65 |
| db.r6g.large | 2 | 16GB | 200 | $175 |
| db.r6g.xlarge | 4 | 32GB | 400 | $350 |
| db.r6g.2xlarge | 8 | 64GB | 800 | 700ドル |
ほとんどのアプリケーションは、64 GB RAM と 8 vCPU で制限に達します。それを超えると、垂直方向のスケーリングには法外なコストがかかります。
ステップ 3: 接続プーリング ($)
Application (50 pods x 20 connections = 1,000 connections)
|
v
PgBouncer (25 database connections, transaction pooling)
|
v
PostgreSQL (25 active connections, manageable)
PgBouncer の構成:
[databases]
app = host=db.example.com port=5432 dbname=production
[pgbouncer]
listen_port = 6432
listen_addr = 0.0.0.0
auth_type = md5
pool_mode = transaction
default_pool_size = 25
max_client_conn = 1000
min_pool_size = 5
reserve_pool_size = 5
reserve_pool_timeout = 3
ステップ 4: リードレプリカ ($$)
リードレプリカは SELECT クエリを処理し、データベース負荷の 60 ~ 90% をプライマリからオフロードします。
アーキテクチャ:
Write queries --> Primary database
|
Replication (async)
|
+----+----+
| |
Read queries --> Replica 1 Replica 2
アプリケーションレベルのルーティング (Drizzle ORM の例):
import { drizzle } from 'drizzle-orm/node-postgres';
import { Pool } from 'pg';
const primaryPool = new Pool({ connectionString: process.env.DATABASE_URL });
const replicaPool = new Pool({ connectionString: process.env.DATABASE_REPLICA_URL });
export const primaryDb = drizzle(primaryPool);
export const replicaDb = drizzle(replicaPool);
// In service code:
// Write operations use primaryDb
async createOrder(data: OrderInput) {
return primaryDb.insert(orders).values(data).returning();
}
// Read operations use replicaDb
async getOrders(organizationId: string) {
return replicaDb.select().from(orders)
.where(eq(orders.organizationId, organizationId))
.orderBy(desc(orders.createdAt));
}
レプリケーションの遅延に関する考慮事項: 非同期レプリケーションでは遅延が発生します (通常は 10 ~ 100 ミリ秒)。書き込み直後にレプリカから読み取ると、古いデータが返される場合があります。同じユーザー フロー内の書き込みに続く読み取りにはプライマリを使用します。
ステップ 5: キャッシュ ($$)
Redis キャッシュにより、データベース クエリの繰り返しが完全に排除されます。
async getProduct(id: string): Promise<Product> {
const cacheKey = `product:${id}`;
// Check cache first
const cached = await this.redis.get(cacheKey);
if (cached) return JSON.parse(cached);
// Cache miss: query database
const product = await this.db.select().from(products)
.where(eq(products.id, id))
.limit(1);
// Cache for 5 minutes
await this.redis.setex(cacheKey, 300, JSON.stringify(product[0]));
return product[0];
}
キャッシュ無効化戦略: 書き込み時に無効化します。製品をアップデートした場合は、キャッシュキーを削除してください。ライトスルー (データベースがキャッシュを管理する) ではなく、キャッシュアサイド パターン (アプリケーションがキャッシュを管理する) を使用します。
ステップ 6: 水平シャーディング ($$$)
シャーディングは、シャード キーに基づいて複数のデータベース インスタンスにデータを分散します。
| シャーディング戦略 | 説明 | 最適な用途 |
|---|---|---|
| ハッシュベース | シャード キーをハッシュし、均等に分散します。均一なデータ分布 | |
| 範囲ベース | シャードに範囲を割り当てる (例: A-M、N-Z) | 時系列、地理データ |
| テナントベース | テナント/組織ごとに 1 つのシャード | マルチテナントSaaS |
シャードを行う場合:
- 単一データベースが 1 TB を超え、増加中
- 書き込みスループットが単一のプライマリで処理できる量を超えています
- 垂直スケーリングのコストが月額 2,000 ドルを超え、余裕がない
シャードしない場合:
- ステップ 1 ~ 5 をまだ完了していません
- データは単一の 500 GB データベースに収まります
- アプリケーションではクロスシャードクエリが一般的です
PostgreSQL 固有の最適化
パーティショニング (シャーディング前)
PostgreSQL のテーブル パーティショニングは、単一の論理テーブルを維持しながら、大きなテーブルを小さな物理テーブルに分割します。
-- Partition orders by month
CREATE TABLE orders (
id UUID PRIMARY KEY,
organization_id UUID NOT NULL,
created_at TIMESTAMP NOT NULL,
total DECIMAL(10,2)
) PARTITION BY RANGE (created_at);
CREATE TABLE orders_2026_01 PARTITION OF orders
FOR VALUES FROM ('2026-01-01') TO ('2026-02-01');
CREATE TABLE orders_2026_02 PARTITION OF orders
FOR VALUES FROM ('2026-02-01') TO ('2026-03-01');
CREATE TABLE orders_2026_03 PARTITION OF orders
FOR VALUES FROM ('2026-03-01') TO ('2026-04-01');
PostgreSQL は関連するパーティションのみをスキャンするため、パーティショニングにより、大きなテーブルでの時間範囲クエリのパフォーマンスが 10 ~ 100 倍向上します。
掃除機とメンテナンス
-- Check table bloat
SELECT
schemaname,
relname,
n_live_tup,
n_dead_tup,
round(n_dead_tup::numeric / greatest(n_live_tup, 1) * 100, 2) AS dead_pct
FROM pg_stat_user_tables
WHERE n_dead_tup > 1000
ORDER BY n_dead_tup DESC;
書き込み量の多いテーブルに対して自動バキュームを積極的に構成します。
ALTER TABLE orders SET (
autovacuum_vacuum_threshold = 100,
autovacuum_vacuum_scale_factor = 0.05,
autovacuum_analyze_threshold = 50,
autovacuum_analyze_scale_factor = 0.02
);
データベースのパフォーマンスの監視
これらのメトリクスを追跡して、いつ、どのようにスケールするかを理解します。
| メトリック | ツール | アラートしきい値 |
|---|---|---|
| クエリレイテンシ (P95) | pg_stat_statements | >500ミリ秒 |
| アクティブな接続 | pg_stat_activity | 最大値の >80% |
| キャッシュ ヒット率 | pg_stat_データベース | <95% |
| レプリケーションの遅延 | pg_stat_replication | >1秒 |
| テーブルの肥大化 | pg_stat_user_tables | >20% のデッドタプル |
| ディスク I/O 待機 | iostat / CloudWatch | >20ms |
キャッシュ ヒット率が 95% を下回る場合は、より多くのメモリが必要であることを示す最も強力な指標です。 shared_buffers と effective_cache_size を増やすことは、多くの場合、リードレプリカを追加するよりも安価で高速です。
クエリパフォーマンスの追跡
-- Enable pg_stat_statements (postgresql.conf)
-- shared_preload_libraries = 'pg_stat_statements'
-- Find the top 10 most time-consuming queries
SELECT
queryid,
calls,
mean_exec_time::numeric(10,2) AS avg_ms,
total_exec_time::numeric(10,2) AS total_ms,
rows,
query
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 10;
上位 10 のクエリを毎週確認します。頻繁に実行される単一のクエリを最適化するだけでも、データベース全体の負荷を 10 ~ 30% 削減できます。
よくある質問
スケーリングのタイミングをどのように判断すればよいでしょうか?
クエリ遅延 P95 (500 ミリ秒でアラート)、接続使用率 (80% でアラート)、および CPU 使用率 (70% でアラートが継続) の 3 つのメトリクスを監視します。定期的にこれらのしきい値に達している場合は、スケーリングのはしごの次のステップに進んでください。事前に最適化しないでください --- データから指示されたときにスケーリングします。
リードレプリカとキャッシュ --- どちらが先ですか?
まずはキャッシングから始めます。 Redis キャッシュは実装が簡単で、より多くの負荷が排除され (キャッシュ ヒットがデータベースを完全にスキップします)、コストが低くなります。キャッシュ ヒット率がすでに 80% を超えているにもかかわらず、プライマリ データベースが依然としてキャッシュ ミスや書き込み操作による負荷を受けている場合は、リード レプリカを追加します。
データベースのスケーリングは Odoo でどのように機能しますか?
Odoo は PostgreSQL のみを使用します。クエリの最適化から始めます (Odoo はレポート用に複雑なクエリを生成します)。同時ユーザーが 50 人を超えた場合は、接続プール用に PgBouncer を追加します。クエリのレポートにはリードレプリカを使用します (Odoo の --db-replica オプションを構成します)。 ECOSIRE は、データベースのチューニングを含む Odoo パフォーマンスの最適化 を提供します。
マネージド データベース (RDS/Cloud SQL) にはプレミアムを利用する価値がありますか?
はい、ほとんどのビジネスに当てはまります。管理されたデータベースは、自動バックアップ、パッチ適用、フェイルオーバー、監視を処理します。セルフマネージド PostgreSQL に比べて 30 ~ 40% のコスト割増は、節約されたエンジニアリング時間によって相殺されます。例外は、大規模なインスタンスのコストプレミアムがパートタイムの DBA のコストを超える大規模なデプロイメントです。
次に何が起こるか
データベースのスケーリングは、より広範なインフラストラクチャのスケーリング戦略の 1 つのコンポーネントです。静的アセットの場合は CDN 最適化、アプリケーション ポッドの場合は Kubernetes 自動スケーリング、および現実的な条件下でスケーリングの決定を検証するには 負荷テスト と組み合わせます。
データベース最適化のコンサルティングについては ECOSIRE にお問い合わせ、完全なインフラストラクチャ ロードマップについては DevOps ガイド を参照してください。
ECOSIRE が発行 -- 企業が自信を持ってデータ インフラストラクチャを拡張できるように支援します。
執筆者
ECOSIRE TeamTechnical Writing
The ECOSIRE technical writing team covers Odoo ERP, Shopify eCommerce, AI agents, Power BI analytics, GoHighLevel automation, and enterprise software best practices. Our guides help businesses make informed technology decisions.
関連記事
Drizzle ORM によるゼロダウンタイムのデータベース移行
Drizzle ORM を使用して、ダウンタイムなしでデータベース移行を実行します。エキスパンド コントラクト パターン、下位互換性のあるスキーマ変更、ロールバック戦略、PostgreSQL の CI/CD 統合について説明します。
PostgreSQL を使用した Drizzle ORM: 完全ガイド
PostgreSQL を使用した Drizzle ORM の完全ガイド: スキーマ設計、移行、タイプ セーフ クエリ、関係、トランザクション、TypeScript アプリの運用パターン。
SaaS およびテクノロジー企業向け ERP: 大規模な運用
SaaS およびテクノロジー企業向けの ERP の完全ガイド - サブスクリプション収益、従業員数の増加、ベンダー調達、および大規模な財務報告の管理。