- Published on
MongoDB ShardingとReplica Set運用ガイド:クラスター設計からトラブルシューティングまで
- Authors
- Name
- はじめに
- Shardingアーキテクチャ概要
- Shard Key選択戦略
- Replica Set構成とフェイルオーバー
- Chunkマイグレーションとバランサー
- 読み取り/書き込みConcern設定
- モニタリングとパフォーマンスチューニング
- バックアップとリカバリ
- トラブルシューティング事例
- 運用チェックリスト
- 参考資料

はじめに
MongoDBは大規模データ処理と高可用性のために、ShardingとReplica Setという2つの核心的な分散メカニズムを提供している。Shardingはデータを複数のサーバーに水平分散してストレージ容量とスループットを拡張し、Replica Setはデータを複製して障害発生時の自動フェイルオーバーを保証する。プロダクション環境でこの2つのメカニズムを正しく組み合わせることが、安定したMongoDB運用の基盤となる。
本記事では、Shardingアーキテクチャの構成要素からShard Key選択戦略、Replica Setフェイルオーバーメカニズム、Chunkマイグレーションとバランサー管理、読み取り/書き込みConcern設定、モニタリングとパフォーマンスチューニング、バックアップ/リカバリ、そして実践的なトラブルシューティング事例まで、プロダクションクラスター運用に必要な全体知識を網羅する。
Shardingアーキテクチャ概要
MongoDB Sharded Clusterは3つの構成要素で成り立っている。
mongos(Query Router):クライアントリクエストを適切なシャードにルーティングするプロキシの役割を果たす。ステートレスなので、複数台をロードバランサーの背後にデプロイできる。
Config Server:クラスターのメタデータ(シャード一覧、チャンク範囲、データ分布情報)を格納するReplica Set。MongoDB 3.4以降、必ずReplica Setとして構成する必要がある。
Shard:実際のデータを格納するReplica Set。各シャードは全体データの一部(チャンク)を担当する。
# Sharded Cluster 基本構成(最小構成)
#
# Client
# |
# v
# mongos (Router) x 2 -- ロードバランサーの背後に配置
# |
# v
# Config Server Replica Set (3 nodes)
# |
# +--- Shard 1 Replica Set (Primary + 2 Secondary)
# +--- Shard 2 Replica Set (Primary + 2 Secondary)
# +--- Shard 3 Replica Set (Primary + 2 Secondary)
#
# 合計ノード数: 2 (mongos) + 3 (config) + 9 (shard) = 14
Sharded Cluster初期構成
// mongosに接続してShard追加
sh.addShard('shard1/mongo-shard1-a:27018,mongo-shard1-b:27018,mongo-shard1-c:27018')
sh.addShard('shard2/mongo-shard2-a:27018,mongo-shard2-b:27018,mongo-shard2-c:27018')
sh.addShard('shard3/mongo-shard3-a:27018,mongo-shard3-b:27018,mongo-shard3-c:27018')
// データベースのShardingを有効化
sh.enableSharding('myapp')
// コレクションにShard Keyを指定(Hashed)
sh.shardCollection('myapp.orders', { customerId: 'hashed' })
// コレクションにShard Keyを指定(Ranged)
sh.shardCollection('myapp.logs', { timestamp: 1 })
// クラスター状態確認
sh.status()
Shard Key選択戦略
Shard Keyは一度設定すると変更が非常に難しいため(MongoDB 5.0からreshardingサポート)、設計段階で慎重に選択する必要がある。良いShard Keyは、高いカーディナリティ(cardinality)、低い頻度(frequency)、非単調(non-monotonic)な特性を持つべきだ。
Shard Key戦略比較表
| 戦略 | 説明 | メリット | デメリット | 適合するユースケース |
|---|---|---|---|---|
| Hashed Sharding | フィールド値のハッシュで分散 | データ均等分配、ホットスポット防止 | 範囲クエリ非効率、全シャードスキャン必要 | 高頻度書き込み、範囲クエリ不要 |
| Ranged Sharding | フィールド値の範囲で分散 | 範囲クエリ効率的、特定シャードのみアクセス | ホットスポット可能、単調増加キー問題 | 時間ベースログ、範囲クエリ頻繁 |
| Zone Sharding | 地域/条件別データ配置 | データローカリティ、規制準拠 | 設定複雑、不均衡の可能性 | マルチリージョン、データ主権要件 |
| Compound Key | 複合フィールド組み合わせ | カーディナリティ向上、クエリ最適化 | 設計複雑度増加 | 単一フィールドでは不十分な場合 |
Shard Key設定例
// 1. Hashed Shard Key - 均等分配が最優先の場合
sh.shardCollection('myapp.users', { _id: 'hashed' })
// 2. Compound Shard Key - カーディナリティとクエリパターンの同時充足
sh.shardCollection('myapp.events', { tenantId: 1, createdAt: 1 })
// 3. Zone Sharding - 地域別データ分離
sh.addShardToZone('shard1', 'JP')
sh.addShardToZone('shard2', 'US')
sh.addShardToZone('shard3', 'EU')
sh.updateZoneKeyRange(
'myapp.users',
{ region: 'JP', _id: MinKey },
{ region: 'JP', _id: MaxKey },
'JP'
)
sh.updateZoneKeyRange(
'myapp.users',
{ region: 'US', _id: MinKey },
{ region: 'US', _id: MaxKey },
'US'
)
// 4. MongoDB 8.0+ resharding - Shard Key変更が必要な場合
sh.reshardCollection('myapp.orders', { customerId: 1, orderId: 1 })
Shard Keyアンチパターン
単調増加するフィールド(例:ObjectId、timestamp)をRanged Shard Keyとして使用すると、新しいデータが常に最後のチャンクに集中し、ホットシャード(Hot Shard)が発生する。この場合、Hashed Shardingを使用するか複合キーを組み合わせる必要がある。また、カーディナリティが低すぎるフィールド(例:boolean、status列挙型)はデータを均等に分散できず、ジャンボチャンク(Jumbo Chunk)を引き起こす可能性がある。
Replica Set構成とフェイルオーバー
Replica Set初期化
// PrimaryでReplica Set初期化
rs.initiate({
_id: 'shard1',
members: [
{ _id: 0, host: 'mongo-shard1-a:27018', priority: 10 },
{ _id: 1, host: 'mongo-shard1-b:27018', priority: 5 },
{ _id: 2, host: 'mongo-shard1-c:27018', priority: 1 },
],
settings: {
electionTimeoutMillis: 10000, // デフォルト10秒
heartbeatTimeoutSecs: 10, // ハートビートタイムアウト
chainingAllowed: true, // Secondary間チェイニング許可
},
})
// Replica Set状態確認
rs.status()
// レプリケーション遅延確認
rs.printReplicationInfo()
rs.printSecondaryReplicationInfo()
フェイルオーバーメカニズム
MongoDB Replica Setのフェイルオーバーは、Raftベースの合意アルゴリズムで動作する。Primaryが応答しなくなると、残りのメンバーが選挙を開始し、過半数以上の投票を得たメンバーが新しいPrimaryとして選出される。
選挙条件:
- 過半数(majority)のメンバーが相互通信可能であること
- priorityが0のメンバーはPrimaryになれない
- hiddenメンバーやdelayedメンバーは投票のみ可能でPrimary候補から除外される
- electionTimeoutMillis(デフォルト10秒)以内にPrimaryと通信できない場合、選挙が開始される
// 手動フェイルオーバー実行(メンテナンス時)
rs.stepDown(60) // 60秒間Primary役割を放棄
// 特定メンバーをPrimaryに昇格させるためpriority調整
cfg = rs.conf()
cfg.members[1].priority = 100
rs.reconfig(cfg)
// Hiddenメンバー設定(バックアップ専用)
cfg = rs.conf()
cfg.members[2].hidden = true
cfg.members[2].priority = 0
rs.reconfig(cfg)
// Delayedメンバー設定(データ復旧用、1時間遅延)
cfg = rs.conf()
cfg.members[2].secondaryDelaySecs = 3600
cfg.members[2].hidden = true
cfg.members[2].priority = 0
rs.reconfig(cfg)
Chunkマイグレーションとバランサー
バランサーの動作原理
バランサーはConfig ServerのPrimaryで実行され、シャード間のチャンク数の差が閾値(migration threshold)を超えるとチャンクを移動する。n個のシャードがあるクラスターでは、最大n/2個の同時マイグレーションが可能である。
// バランサー状態確認
sh.getBalancerState()
sh.isBalancerRunning()
// バランサー停止/開始
sh.stopBalancer()
sh.startBalancer()
// バランサー時間ウィンドウ設定(深夜2-6時のみ動作)
db.adminCommand({
balancerStart: 1,
condition: {
activeWindow: { start: '02:00', stop: '06:00' },
},
})
// 特定コレクションのバランシング無効化
sh.disableBalancing('myapp.orders')
// チャンク手動移動
sh.moveChunk('myapp.orders', { customerId: 'user123' }, 'shard3')
// 現在のマイグレーション状態確認
db.adminCommand({ currentOp: true, desc: /migrate/ })
チャンク管理
// チャンクサイズ変更(デフォルト128MB、MongoDB 7.0+)
use config
db.settings.updateOne(
{ _id: "chunksize" },
{ $set: { value: 64 } },
{ upsert: true }
)
// ジャンボチャンク確認
db.chunks.find({ jumbo: true }).forEach(function(chunk) {
print("Jumbo chunk: " + chunk.ns + " on " + chunk.shard)
})
// ジャンボチャンク強制分割試行
sh.splitAt("myapp.orders", { customerId: "splitPoint" })
// チャンク分布確認
db.chunks.aggregate([
{ $group: { _id: "$shard", count: { $sum: 1 } } },
{ $sort: { count: -1 } }
])
読み取り/書き込みConcern設定
Read Preferenceモード比較
| モード | 読み取り対象 | 一貫性 | レイテンシ | 適合するユースケース |
|---|---|---|---|---|
| primary | Primaryのみ | 強い一貫性 | 基準値 | リアルタイムデータ必要 |
| primaryPreferred | Primary優先、不可時Secondary | ほぼ強い一貫性 | 基準値 | Primary障害への備え |
| secondary | Secondaryのみ | 結果整合性 | 低い(最寄りノード) | レポート、分析クエリ |
| secondaryPreferred | Secondary優先、不可時Primary | ほぼ結果整合性 | 低い | 読み取り負荷分散 |
| nearest | ネットワーク遅延最小ノード | 結果整合性 | 最小 | 地理分散デプロイ |
Write Concernレベル比較
| Write Concern | 説明 | 耐久性 | パフォーマンス | 適合するユースケース |
|---|---|---|---|---|
| w: 0 | 応答待ちなし | 非常に低い | 最高 | ログ、メトリクス(損失許容) |
| w: 1 | Primary確認のみ | 普通 | 高い | 一般的な書き込み |
| w: "majority" | 過半数確認 | 高い | 普通 | 重要データ(推奨デフォルト) |
| w: N | N個ノード確認 | 非常に高い | 低い | 金融等の厳格な要件 |
| j: true | ジャーナル書き込み確認 | 最高 | 低い | 耐久性最優先 |
// 読み取り/書き込みConcern設定例
// 1. コレクションレベルでRead Preference設定
db.orders.find({ status: 'pending' }).readPref('secondaryPreferred')
// 2. 接続文字列での設定
// mongodb://mongos1:27017,mongos2:27017/myapp?readPreference=secondaryPreferred&w=majority
// 3. Write Concern指定
db.orders.insertOne(
{ customerId: 'user123', amount: 50000 },
{ writeConcern: { w: 'majority', j: true, wtimeout: 5000 } }
)
// 4. グローバルデフォルトRead/Write Concern設定
db.adminCommand({
setDefaultRWConcern: 1,
defaultWriteConcern: { w: 'majority', j: true },
defaultReadConcern: { level: 'majority' },
})
モニタリングとパフォーマンスチューニング
重要モニタリングクエリ
// 1. シャード別データ分布確認
db.orders.getShardDistribution()
// 2. 長時間実行中のオペレーション確認
db.currentOp({ secs_running: { $gt: 10 } })
// 3. スロークエリプロファイリング有効化
db.setProfilingLevel(1, { slowms: 100 })
// 4. スロークエリ分析
db.system.profile
.find({
millis: { $gt: 100 },
ns: /myapp\.orders/,
})
.sort({ ts: -1 })
.limit(10)
// 5. サーバー状態確認
db.serverStatus().opcounters // オペレーションカウンター
db.serverStatus().connections // 接続数
db.serverStatus().wiredTiger.cache // キャッシュ状態
// 6. Replica Setレプリケーション遅延確認
db.adminCommand({ replSetGetStatus: 1 }).members.forEach(function (m) {
if (m.stateStr === 'SECONDARY') {
var lag = (new Date() - m.optimeDate) / 1000
print(m.name + ' lag: ' + lag + 's')
}
})
// 7. インデックス使用統計
db.orders.aggregate([{ $indexStats: {} }])
// 8. Config Serverメタデータ整合性検証(MongoDB 7.0+)
db.adminCommand({ checkMetadataConsistency: 1 })
パフォーマンスチューニングチェックリスト
- インデックスカバリング:Shard Keyを含む複合インデックスでクエリが単一シャードで処理されるようにする
- Scatter-Gather最小化:Shard Keyをクエリフィルターに含めて特定シャードのみを対象にする
- Connection Pooling:mongos接続プールサイズを適切に設定する(デフォルトmaxPoolSize=100)
- Read Preference活用:分析クエリはSecondaryに分散する
- チャンクサイズ調整:書き込みが多いワークロードはチャンクサイズを縮小してマイグレーション影響を最小化する
- WiredTigerキャッシュ:cacheSizeGBをシステムRAMの50-60%に設定する
バックアップとリカバリ
mongodump/mongorestoreによるバックアップ
# 1. バックアップ前にバランサー停止(シャードクラスター)
mongosh --host mongos1:27017 --eval "sh.stopBalancer()"
# 2. 個別シャードバックアップ(--oplogでポイントインタイム一貫性確保)
mongodump --host shard1/mongo-shard1-a:27018 \
--oplog --out /backup/shard1-$(date +%Y%m%d)
mongodump --host shard2/mongo-shard2-a:27018 \
--oplog --out /backup/shard2-$(date +%Y%m%d)
# 3. Config Serverバックアップ
mongodump --host configRS/config1:27019 \
--oplog --out /backup/config-$(date +%Y%m%d)
# 4. バランサー再開
mongosh --host mongos1:27017 --eval "sh.startBalancer()"
# 5. リストア(--oplogReplayでポイントインタイム復旧)
mongorestore --host shard1/mongo-shard1-a:27018 \
--oplogReplay /backup/shard1-20260313
ポイントインタイムリカバリ(PITR)とOplog
// Oplogサイズと保持時間確認
db.getReplicationInfo()
// 出力例:
// configured oplog size: 2048MB
// log length start to end: 172800secs (48hrs)
// oplog first event time: ...
// oplog last event time: ...
// Oplogサイズ変更(最低48時間以上の保持を推奨)
db.adminCommand({ replSetResizeOplog: 1, size: 4096 }) // 4GB
プロダクションバックアップ推奨事項:
- 小規模クラスターではmongodumpで十分だが、大規模環境ではファイルシステムスナップショット(LVM/EBS)またはMongoDB Atlas Backupの使用を推奨
- Oplogサイズは最低48時間以上の書き込みを保持できるよう設定する
- Delayed Secondaryメンバーを活用すると、論理的エラー(誤ってデータ削除など)から迅速に復旧できる
- バックアップリストアテストを定期的に実施する(最低月1回)
トラブルシューティング事例
1. Shard Keyホットスポット
症状:特定シャードのCPU/ディスクI/Oのみ高く、残りのシャードはアイドル状態
原因:単調増加フィールド(ObjectId、timestamp)をRanged Shard Keyとして使用し、全ての新規書き込みが最後のチャンクに集中
解決策:
- Hashed Shard Keyでreshardingを実行(MongoDB 5.0+)
- Compound Shard Keyを使用してカーディナリティを向上
- MongoDB 8.0の
sh.shardAndDistributeCollection()で高速再分配
2. Split-brain(ネットワークパーティション)
症状:2つ以上のノードが同時にPrimaryとして動作し、データ不整合が発生する可能性
原因:ネットワークパーティションによりReplica Setが2つのグループに分離し、各グループが別々にPrimaryを選出
解決策:
- Replica Setメンバーを奇数(3、5、7)に維持し、常に過半数グループが1つだけ存在するよう構成
- ネットワークパーティション時、過半数に属さないPrimaryは自動的にSecondaryに降格される
w: "majority"Write Concernを使用して、過半数に複製されない書き込みを防止
3. Chunkマイグレーション失敗
症状:バランサーログに「Data transfer error」または「WriteConcernFailed」エラー
原因:レプリケーション遅延が深刻、ネットワーク帯域不足、または対象シャードのディスク容量不足
解決策:
// _secondaryThrottle有効化でレプリケーション同期を保証
use config
db.settings.updateOne(
{ _id: "balancer" },
{ $set: { "_secondaryThrottle": { w: 2 } } },
{ upsert: true }
)
// rangeDeleterバッチサイズ縮小
db.adminCommand({
setParameter: 1,
rangeDeleterBatchSize: 32,
rangeDeleterBatchDelayMS: 100
})
4. Replica Lag(レプリケーション遅延)
症状:SecondaryのoptimeDateがPrimaryより数十秒以上遅れている
原因:大量書き込み負荷、遅いディスクI/O、インデックスビルド、ネットワークボトルネック
解決策:
- WiredTigerキャッシュサイズを確認し、必要に応じて増加
- Secondaryで実行中の重いクエリを確認して中断
- ネットワーク帯域を確認しOplog転送経路を最適化
- インデックスビルドはローリング方式(1ノードずつ順次)で実行
運用チェックリスト
デプロイ前:
- Shard Keyをクエリパターン、カーディナリティ、書き込み分布に基づいて選択したか
- 各Replica Setが最低3メンバー(奇数)で構成されているか
- Config Serverが別途Replica Set(3ノード)として構成されているか
- mongosが2台以上でロードバランサーの背後にあるか
- 認証(keyFileまたはx.509)とネットワーク暗号化(TLS)が設定されているか
日常運用:
- バランサーが正常動作しチャンクが均等分布されているか
- Replica Lagが閾値(例:10秒)以内か
- 接続数がmaxIncomingConnections上限に対して適切か
- スロークエリログを定期的に分析しているか
- ディスク使用率が70%以下か
バックアップ/リカバリ:
- 自動バックアップが日次で実行されているか
- Oplogサイズが最低48時間以上のデータを保持しているか
- バックアップリストアテストを直近30日以内に実施したか
- Delayed Secondaryが構成されて論理的エラーに備えているか
障害対策:
- 自動フェイルオーバーテストを直近四半期内に実施したか
- モニタリングアラートがReplica Lag、ディスク使用率、接続数に対して設定されているか
- Split-brainシナリオに対する復旧手順が文書化されているか
参考資料
- MongoDB公式ドキュメント - Sharding
- MongoDB公式ドキュメント - Replication
- MongoDB公式ドキュメント - Shard Key選択ガイド
- MongoDB公式ドキュメント - Sharded Cluster Balancer
- MongoDB公式ドキュメント - Backup and Restore
- MongoDB公式ドキュメント - Troubleshoot Sharded Clusters
- Percona Blog - When Should I Enable MongoDB Sharding
- Severalnines - MongoDB Backup Management Tips for Sharded Clusters