MongoDB分片 #
一、分片概述 #
1.1 什么是分片 #
分片(Sharding)是将数据分散存储在多个服务器上的方法,实现水平扩展。
text
分片集群架构
┌─────────────────────────────────────┐
│ mongos (路由) │
│ 路由查询请求 │
└──────────────┬──────────────────────┘
│
┌───────┼───────┐
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐
│ Shard 1 │ │ Shard 2 │ │ Shard 3 │
│ (分片1) │ │ (分片2) │ │ (分片3) │
└─────────┘ └─────────┘ └─────────┘
│ │ │
└───────┼───────┘
▼
┌─────────────────────────────────────┐
│ Config Servers │
│ 存储集群元数据 │
└─────────────────────────────────────┘
1.2 分片组件 #
| 组件 | 说明 |
|---|---|
| mongos | 路由进程,转发客户端请求 |
| Shard | 分片服务器,存储数据子集 |
| Config Server | 配置服务器,存储集群元数据 |
1.3 分片优势 #
| 优势 | 说明 |
|---|---|
| 水平扩展 | 增加分片即可扩展容量 |
| 高可用 | 每个分片可以是复制集 |
| 负载均衡 | 数据自动分布到各分片 |
| 透明访问 | 客户端无需关心数据位置 |
二、分片集群部署 #
2.1 部署Config Server #
bash
# 启动Config Server(复制集)
mongod --configsvr --replSet configRs --port 27019 --dbpath /data/config
# 初始化Config Server复制集
mongosh --port 27019
rs.initiate({
_id: "configRs",
configsvr: true,
members: [
{ _id: 0, host: "config1:27019" },
{ _id: 1, host: "config2:27019" },
{ _id: 2, host: "config3:27019" }
]
})
2.2 部署Shard #
bash
# 启动Shard(复制集)
mongod --shardsvr --replSet shard1 --port 27018 --dbpath /data/shard1
# 初始化Shard复制集
mongosh --port 27018
rs.initiate({
_id: "shard1",
members: [
{ _id: 0, host: "shard1-1:27018" },
{ _id: 1, host: "shard1-2:27018" },
{ _id: 2, host: "shard1-3:27018" }
]
})
2.3 部署mongos #
bash
# 启动mongos
mongos --configdb configRs/config1:27019,config2:27019,config3:27019 --port 27017
2.4 添加分片 #
javascript
// 连接mongos
mongosh --port 27017
// 添加分片
sh.addShard("shard1/shard1-1:27018,shard1-2:27018,shard1-3:27018")
sh.addShard("shard2/shard2-1:27018,shard2-2:27018,shard2-3:27018")
// 查看分片状态
sh.status()
三、启用分片 #
3.1 启用数据库分片 #
javascript
// 启用数据库分片
sh.enableSharding("mydb")
3.2 对集合分片 #
javascript
// 对集合分片
sh.shardCollection("mydb.users", { userId: 1 })
// 使用哈希分片
sh.shardCollection("mydb.users", { userId: "hashed" })
// 使用复合分片键
sh.shardCollection("mydb.orders", { userId: 1, orderId: 1 })
3.3 查看分片状态 #
javascript
// 查看分片状态
sh.status()
// 查看集合分片信息
db.users.getShardDistribution()
// 输出示例
Shard shard1 at shard1/...
data: 100MB docs: 10000 chunks: 5
estimated data per chunk: 20MB
Shard shard2 at shard2/...
data: 100MB docs: 10000 chunks: 5
estimated data per chunk: 20MB
Totals
data: 200MB docs: 20000 chunks: 10
四、分片键 #
4.1 分片键选择 #
| 考虑因素 | 说明 |
|---|---|
| 基数 | 分片键值应该有足够的区分度 |
| 写入分布 | 写入应该均匀分布到各分片 |
| 查询模式 | 常用查询应该包含分片键 |
| 不可变性 | 分片键值不能修改 |
4.2 范围分片 #
javascript
// 范围分片
sh.shardCollection("mydb.users", { userId: 1 })
// 特点:
// - 数据按分片键值范围分布
// - 支持范围查询
// - 可能导致数据分布不均
4.3 哈希分片 #
javascript
// 哈希分片
sh.shardCollection("mydb.users", { userId: "hashed" })
// 特点:
// - 数据均匀分布
// - 不支持范围查询优化
// - 写入分布均匀
4.4 复合分片键 #
javascript
// 复合分片键
sh.shardCollection("mydb.orders", { userId: 1, createdAt: 1 })
// 特点:
// - 结合多个字段
// - 第一个字段决定分布
// - 后续字段用于范围查询
五、Chunk管理 #
5.1 Chunk概述 #
Chunk是分片的数据块,默认64MB。
text
Chunk分布
┌─────────────────────────────────────┐
│ Chunk 1: [min, 100) │
├─────────────────────────────────────┤
│ Chunk 2: [100, 200) │
├─────────────────────────────────────┤
│ Chunk 3: [200, 300) │
├─────────────────────────────────────┤
│ Chunk 4: [300, max) │
└─────────────────────────────────────┘
5.2 Chunk分割 #
javascript
// 手动分割Chunk
sh.splitAt("mydb.users", { userId: 1000 })
// 按数量分割
sh.splitFind("mydb.users", { userId: 500 })
5.3 Chunk迁移 #
javascript
// 手动迁移Chunk
sh.moveChunk("mydb.users", { userId: 1000 }, "shard2")
// 查看迁移状态
sh.isBalancerRunning()
5.4 Balancer管理 #
javascript
// 查看Balancer状态
sh.getBalancerState()
// 启用Balancer
sh.startBalancer()
// 停止Balancer
sh.stopBalancer()
// 设置Balancer窗口
db.settings.updateOne(
{ _id: "balancer" },
{ $set: { activeWindow: { start: "02:00", stop: "06:00" } } },
{ upsert: true }
)
六、分片标签 #
6.1 添加标签 #
javascript
// 为分片添加标签
sh.addShardTag("shard1", "zone1")
sh.addShardTag("shard2", "zone2")
6.2 配置标签范围 #
javascript
// 配置标签范围
sh.addTagRange(
"mydb.users",
{ userId: MinKey },
{ userId: 1000 },
"zone1"
)
sh.addTagRange(
"mydb.users",
{ userId: 1000 },
{ userId: MaxKey },
"zone2"
)
6.3 删除标签 #
javascript
// 删除标签
sh.removeShardTag("shard1", "zone1")
// 删除标签范围
sh.removeTagRange(
"mydb.users",
{ userId: MinKey },
{ userId: 1000 }
)
七、分片查询 #
7.1 定向查询 #
javascript
// 包含分片键的查询(定向查询)
db.users.find({ userId: 123 }) // 只查询特定分片
// 包含分片键前缀的查询
db.orders.find({ userId: 123 }) // 只查询特定分片
7.2 广播查询 #
javascript
// 不包含分片键的查询(广播查询)
db.users.find({ name: "John" }) // 查询所有分片
7.3 查询优化 #
javascript
// 使用分片键优化查询
db.users.find({ userId: 123, name: "John" })
// 使用索引优化广播查询
db.users.createIndex({ name: 1 })
八、分片管理 #
8.1 添加分片 #
javascript
// 添加新分片
sh.addShard("shard3/shard3-1:27018,shard3-2:27018,shard3-3:27018")
8.2 删除分片 #
javascript
// 删除分片
sh.removeShard("shard3")
// 查看删除进度
db.adminCommand({ removeShard: "shard3" })
8.3 查看分片信息 #
javascript
// 查看分片列表
db.adminCommand({ listShards: 1 })
// 查看数据库分片信息
db.adminCommand({ enableSharding: "mydb" })
// 查看集合分片信息
db.users.getShardDistribution()
九、分片监控 #
9.1 集群状态 #
javascript
// 查看集群状态
sh.status()
// 查看详细信息
db.adminCommand({ shardingState: 1 })
9.2 Chunk统计 #
javascript
// 查看Chunk统计
db.chunks.find({ ns: "mydb.users" }).count()
// 查看Chunk分布
db.chunks.aggregate([
{ $match: { ns: "mydb.users" } },
{ $group: { _id: "$shard", count: { $sum: 1 } } }
])
9.3 Balancer状态 #
javascript
// 查看Balancer状态
sh.isBalancerRunning()
sh.getBalancerState()
// 查看迁移历史
db.changelog.find({ what: "chunk migrated" }).sort({ time: -1 }).limit(10)
十、最佳实践 #
10.1 分片键选择 #
text
分片键选择原则
1. 高基数
- 分片键值应该有足够的区分度
- 避免使用低基数字段(如性别)
2. 写入分布
- 写入应该均匀分布到各分片
- 避免使用单调递增的字段
3. 查询模式
- 常用查询应该包含分片键
- 减少广播查询
4. 不可变性
- 分片键值不能修改
- 选择稳定的字段
10.2 部署建议 #
text
生产环境部署建议
1. Config Server
- 3个节点的复制集
- 使用SSD存储
2. Shard
- 每个分片是3节点复制集
- 跨机房部署
3. mongos
- 多个mongos实例
- 负载均衡
4. 监控
- 监控Chunk分布
- 监控Balancer状态
10.3 性能优化 #
javascript
// 1. 使用合适的分片键
sh.shardCollection("mydb.users", { userId: "hashed" })
// 2. 预分割Chunk
sh.splitAt("mydb.users", { userId: 1000 })
sh.splitAt("mydb.users", { userId: 2000 })
// 3. 禁用Balancer(批量导入时)
sh.stopBalancer()
// 导入数据
sh.startBalancer()
十一、总结 #
分片操作速查表:
| 操作 | 命令 |
|---|---|
| 启用数据库分片 | sh.enableSharding(“db”) |
| 对集合分片 | sh.shardCollection(“db.coll”, {…}) |
| 添加分片 | sh.addShard(“shardRs/host:port”) |
| 删除分片 | sh.removeShard(“shard”) |
| 查看状态 | sh.status() |
| 迁移Chunk | sh.moveChunk(“ns”, {…}, “shard”) |
分片键类型:
| 类型 | 说明 |
|---|---|
| 范围分片 | 按值范围分布 |
| 哈希分片 | 按哈希值分布 |
| 复合分片键 | 多字段组合 |
下一步,让我们学习用户权限管理!
最后更新:2026-03-27