要点: DBのスケールは、まずどこで資源が競合しているかを特定するところから始まります。
この記事の用語: 接続プール:再利用するDB接続群。レプリカ:読み取りを分担するDB複製。パーティション:ルールに基づくデータ分割。
現場の課題
アプリケーションはDBの接続上限より速く台数を増やせます。大きすぎる接続プール、重複クエリ、長いトランザクションが能力を使い切り、早すぎる分割は運用を複雑にします。
単純な対策だけでは不十分な理由
DBは新しい分割技術がないから遅いのではなく、接続競合、重いクエリ、長いトランザクションで詰まることが多いものです。実測前のレプリカやシャーディングは問題を隠します。
処理フロー
有限Unit of Work→Pooler
接続 Budget→Primary
書き込みと整合読み取り→レプリカ・パーティション
計測済みOffload
アーキテクチャ上の判断
接続をGlobal Budget化
マイグレーションと障害対応用Reserveを残し、Hard 上限を配分します。
Use CaseごとのConsistency
Balanceや読み取り-after-書き込みはPrimary、許容時のみレプリカを使います。
Pruning目的のパーティション
スキャン削減やRetentionに効くキーを選びます。
さらに深く考える
単独遅延より総Work
短い高頻度クエリをFrequencyとRowsで評価します。
シャーディングは所有責任設計
Shard キー、Cross-shard、一意性、Rebalanceを事前設計します。
実装手順
- システム全体の接続上限を決め、API、ワーカー、デプロイへ配分します。
- 総コストが大きいクエリから、実行プランと本番に近いデータを使って改善します。
- 整合性要件とルーティング規則が明確になってからレプリカやパーティションを追加します。
コード例: 有限トランザクションとConsistency-aware 読み取り
with db.transaction(timeout = 800ms) as tx:
order = tx.orders.lock(orderId)
tx.orders.update(orderId, nextState)
readTarget = PRIMARY if session.justWrote else REPLICA
return query(readTarget, tenantId, orderId)ネットワーク呼び出しをトランザクション外へ出し、外部TimeoutでLockを保持しないようにします。
想定しておく障害
- デプロイ Surgeが接続上限を超えます。
- レプリカ Lagが購入直後に古い権限を返します。
- インデックス ビルドが本番 書き込みを飽和させます。
監視すべきこと
| 指標 | 何が分かるか |
|---|---|
| Active・Waiting 接続 | 実処理とプール競合を分離します。 |
| クエリ Total Time・Rows | Fleet全体コストで優先します。 |
| Lock Wait・レプリカ Lag | 整合性とTail 遅延 リスクです。 |
設計の検証方法
- 最大レプリカとデプロイ Surgeを試験します。
- レプリカ Lagで読み取り-after-書き込みを試験します。
- 実Volume相当でインデックス ビルド影響を測ります。
本番導入の進め方
クエリと接続を可視化し、大きすぎるプールと高コストなクエリを先に直します。古い読み取りを許容できる一経路でレプリカを試し、パーティションはロールバックと照合を持つ別プロジェクトとして扱います。
本番前チェックリスト
- 全トランザクションにTimeoutと小さなScopeを持たせます。
- インデックスは読み取り効果と書き込み コストで評価します。
- レプリカ Lagをアプリケーション Routingへ反映します。
まとめ
拡張可能なDBは、分割する前に資源上限と予測可能なアクセス パターンで守られています。
