MongoDB 分片

MongoDB 分片(Sharding)全面指南

MongoDB 分片(Sharding) 是实现 水平扩展(Horizontal Scaling) 的核心机制,将数据分布到多个 分片(Shard) 上,每个分片是一个 副本集(Replica Set),通过 路由进程(mongos) 实现透明访问。


一、核心组件

组件职责是否高可用
mongos查询路由、数据合并、客户端入口可多实例
config server存储元数据(chunk 位置、配置)必须是 3 节点副本集
shard实际存储数据的副本集每个 shard 建议 3 节点副本集
客户端 → mongos → config servers + shards

二、架构图示例

                     ┌─────────────────┐
                     │     Client      │
                     └─────────┬─────────┘
                           ▼
                  ┌─────────────────┐
                  │     mongos      │
                  └───────┬───┬─────┘
                          ▼   ▼
               ┌──────────────────────────┐
               │    Config Servers (RS)   │
               │  [csrs: 3 nodes]         │
               └───────┬──────────────────┘
                       ▼
        ┌──────────────┴───────────────┐
        ▼                               ▼
┌───────────────┐               ┌───────────────┐
│   Shard 1     │               │   Shard 2     │
│ (replset rs1) │               │ (replset rs2) │
│ [p,s,s]       │               │ [p,s,s]       │
└───────────────┘               └───────────────┘

三、分片键(Shard Key)选择原则

原则说明
高基数(Cardinality)值分布均匀,避免热点(如 user_id 优于 status
低频率变更避免 update 触发 chunk 迁移
支持查询模式常用过滤字段(如 user_id, createdAt
避免单调递增ObjectId 导致写入集中到最后一个 chunk

推荐分片键

场景推荐键
用户数据user_id
时间序列{ user_id: 1, createdAt: 1 }
多租户tenant_id
地理位置location(hashed)

四、搭建分片集群(完整实战)

1. 目录结构

mkdir -p shard/{config,shard1,shard2}/{1,2,3} mongos

2. 启动 Config Server(副本集 csrs

# config1.conf
mongod --configsvr --replSet csrs --bind_ip_all --port 27019 --dbpath shard/config/1

# config2.conf / config3.conf 类似,端口 27020, 27021
// 初始化 csrs
rs.initiate({
  _id: "csrs",
  configsvr: true,
  members: [
    { _id: 0, host: "localhost:27019" },
    { _id: 1, host: "localhost:27020" },
    { _id: 2, host: "localhost:27021" }
  ]
})

3. 启动 Shard 1 和 Shard 2(副本集)

# shard1-1.conf
mongod --shardsvr --replSet shard1 --port 27018 --dbpath shard/shard1/1

# 类似启动 shard1-2(27028), shard1-3(27038)
# shard2: 27048, 27058, 27068
// 初始化 shard1
rs.initiate({
  _id: "shard1",
  members: [
    { _id: 0, host: "localhost:27018" },
    { _id: 1, host: "localhost:27028" },
    { _id: 2, host: "localhost:27038" }
  ]
})

4. 启动 mongos

mongos --configdb csrs/localhost:27019,localhost:27020,localhost:27021 --port 27017

5. 添加分片

// 连接 mongos
mongosh --port 27017

// 添加 shard
sh.addShard("shard1/localhost:27018,localhost:27028,localhost:27038")
sh.addShard("shard2/localhost:27048,localhost:27058,localhost:27068")

6. 启用数据库分片

sh.enableSharding("mydb")  // 启用数据库分片

// 创建集合并指定分片键
sh.shardCollection("mydb.users", { user_id: 1 })

// 或 hashed 分片键(均匀分布)
sh.shardCollection("mydb.events", { device_id: "hashed" })

五、Chunk 与 Balancer

  • 数据按 chunk(默认 64MB)切分
  • Balancer 自动迁移 chunk 保持均衡
sh.status()                    // 查看分片状态
sh.getBalancerState()          // 是否运行
sh.disableBalancing("mydb.users")
sh.enableBalancing("mydb.users")

// 手动迁移 chunk
sh.moveChunk("mydb.logs", { timestamp: ISODate("2025-01-01") }, "shard2")

六、查询路由机制

查询类型路由方式
Targeted包含分片键 → 直达目标 shard
Scatter-Gather无分片键 → 广播所有 shard
Hashed Key计算 hash → 定位
// Targeted(高效)
db.users.find({ user_id: 12345 })

// Scatter-Gather(低效)
db.users.find({ email: "a@x.com" })  // 无索引 + 无分片键

七、高级分片策略

1. 复合分片键(Compound Shard Key)

sh.shardCollection("mydb.orders", { 
  customer_id: 1, 
  order_date: 1 
})

支持 { customer_id: X }{ customer_id: X, order_date: { $gte: ... } }


2. Hashed 分片键

sh.shardCollection("mydb.sessions", { session_id: "hashed" })

适合高并发写入,避免热点


3. 标签感知分片(Tag-Aware Sharding)

// 标记 shard 地域
sh.addTagRange("mydb.users", 
  { region: "US", user_id: MinKey }, 
  { region: "US", user_id: MaxKey }, 
  "US-shard"
)

sh.addShardTag("shard1", "US")

实现 数据本地化


八、性能监控与优化

关键命令

sh.status()                    // 整体概览
db.collection.getShardDistribution()  // 数据分布
sh.balancerCollectionStatus("mydb.users")

// 查看当前 balancer 状态
sh.isBalancerRunning()

关键指标

指标说明
chunks per shard应接近平均
jumbo chunks超过 64MB,需拆分
balancer rounds迁移频率
mongos latency路由延迟

九、常见问题与解决方案

问题原因解决方案
写入热点单调递增分片键改用 hashed
查询慢Scatter-Gather添加分片键索引
Balancer 不工作窗口关闭sh.setBalancerState(true)
Chunk 无法拆分文档太大预分片 / 调整设计
元数据不一致config server 异常重启 csrs

十、生产级最佳实践

建议说明
每个 shard 是 3 节点副本集高可用
config server 单独部署避免竞争
mongos 与应用同机房降低延迟
预分片(Pre-splitting)大批量导入前
定期备份 config DBmongodump -d config

十一、Docker Compose 一键部署(生产可用模板)

version: '3.8'
services:
  config1: &config
    image: mongo:7
    command: --configsvr --replSet csrs --bind_ip_all
    volumes: [./data/config1:/data/db]
  config2: *config
  config3: *config

  shard1-1: &shard
    image: mongo:7
    command: --shardsvr --replSet shard1 --bind_ip_all
    volumes: [./data/shard1-1:/data/db]
  shard1-2: *shard
  shard1-3: *shard

  shard2-1: *shard
  shard2-2: *shard
  shard2-3: *shard

  mongos:
    image: mongo:7
    command: mongos --configdb csrs/config1:27019,config2:27020,config3:27021 --bind_ip_all
    ports: ["27017:27017"]
    depends_on: [config1, config2, config3]

初始化脚本略,可参考上文


十二、学习资源


你想实现什么?

  • 大规模日志系统?
  • 多租户 SaaS?
  • 全球分布式部署?
  • 10 亿+ 数据迁移?

欢迎贴出你的:

  1. 数据量(文档数、增长率)
  2. 查询模式(QPS、过滤字段)
  3. 高可用要求

我可以为你设计 完整分片方案 + 分片键 + 自动化脚本

文章已创建 2371

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部