分片(Sharding)是MongoDB的水平扩展方案,将数据分散存储在多个服务器上,突破单机存储和性能限制。
分片集群包含三个组件:
1. Mongos(路由服务器)
- 接收客户端请求
- 路由查询到正确的分片
- 合并多个分片的结果
- 无状态,可以部署多个
2. Config Servers(配置服务器)
- 存储集群元数据
- 存储分片键范围映射
- 必须是副本集(3个节点)
3. Shards(分片服务器)
- 存储实际数据
- 每个分片应该是副本集
- 可以有多个分片
// 1. 启动配置服务器(3个节点的副本集)
mongod --configsvr --replSet configRS --port 27019 --dbpath /data/config1
mongod --configsvr --replSet configRS --port 27020 --dbpath /data/config2
mongod --configsvr --replSet configRS --port 27021 --dbpath /data/config3
// 初始化配置服务器副本集
mongosh --port 27019
rs.initiate({
_id: "configRS",
configsvr: true,
members: [
{ _id: 0, host: "localhost:27019" },
{ _id: 1, host: "localhost:27020" },
{ _id: 2, host: "localhost:27021" }
]
})
// 2. 启动分片服务器(每个分片是副本集)
// 分片1
mongod --shardsvr --replSet shard1RS --port 27017 --dbpath /data/shard1
// 分片2
mongod --shardsvr --replSet shard2RS --port 27018 --dbpath /data/shard2
// 3. 启动mongos路由服务器
mongos --configdb configRS/localhost:27019,localhost:27020,localhost:27021 --port 27016
// 4. 连接到mongos并添加分片
mongosh --port 27016
sh.addShard("shard1RS/localhost:27017")
sh.addShard("shard2RS/localhost:27018")
// 5. 查看分片状态
sh.status()
分片键决定数据如何分布到不同分片,是分片集群最重要的设计决策。
// 启用数据库分片
sh.enableSharding("mydb")
// 为集合创建分片键索引
db.users.createIndex({ userId: 1 })
// 对集合进行分片
sh.shardCollection("mydb.users", { userId: 1 })
// 1. 范围分片(Range Sharding)
// 按分片键值范围分布数据
sh.shardCollection("mydb.users", { age: 1 })
// 优点:范围查询高效
// 缺点:可能数据分布不均
// 2. 哈希分片(Hashed Sharding)
// 对分片键进行哈希后分布
sh.shardCollection("mydb.users", { userId: "hashed" })
// 优点:数据分布均匀
// 缺点:范围查询需要访问所有分片
// 3. 复合分片键
// 结合多个字段
sh.shardCollection("mydb.orders", { customerId: 1, orderDate: 1 })
// 优点:更灵活的数据分布和查询优化
// 查看分片状态
sh.status()
// 查看集合分片信息
db.users.getShardDistribution()
// 查看分片列表
db.adminCommand({ listShards: 1 })
// 移动chunk
sh.moveChunk("mydb.users", { userId: 12345 }, "shard2RS")
// 分割chunk
sh.splitAt("mydb.users", { userId: 50000 })
// 启用/禁用均衡器
sh.startBalancer()
sh.stopBalancer()
sh.getBalancerState()
// 设置均衡器窗口(只在特定时间运行)
db.settings.update(
{ _id: "balancer" },
{ $set: { activeWindow: { start: "23:00", stop: "06:00" } } },
{ upsert: true }
)
// Chunk是数据分片的基本单位
// 默认大小:64MB(可配置)
// 查看chunk分布
use config
db.chunks.find({ ns: "mydb.users" }).count()
// 修改chunk大小
db.settings.save({ _id: "chunksize", value: 128 }) // 128MB
// 均衡器自动在分片间移动chunk以保持均衡
// 触发条件:分片间chunk数量差异超过阈值
// 将特定数据范围分配到特定分片
// 用于地理位置隔离、硬件分级等场景
// 1. 为分片添加标签
sh.addShardTag("shard1RS", "CN")
sh.addShardTag("shard2RS", "US")
// 2. 定义区域范围
sh.addTagRange(
"mydb.users",
{ country: "CN", userId: MinKey },
{ country: "CN", userId: MaxKey },
"CN"
)
sh.addTagRange(
"mydb.users",
{ country: "US", userId: MinKey },
{ country: "US", userId: MaxKey },
"US"
)
// 中国用户数据存储在shard1,美国用户数据存储在shard2
// 包含分片键的查询(目标查询)
// 只访问相关分片
db.users.find({ userId: 12345 })
// 不包含分片键的查询(广播查询)
// 需要访问所有分片
db.users.find({ email: "user@example.com" })
// 查看查询执行计划
db.users.find({ userId: 12345 }).explain()
// 关键指标:
// - shards: 访问的分片列表
// - nReturned: 返回的文档数
// - executionTimeMillis: 执行时间
// 查看集群状态
sh.status()
// 查看数据分布
db.users.getShardDistribution()
// 查看chunk迁移状态
db.adminCommand({ balancerStatus: 1 })
// 查看当前操作
db.currentOp()
// 监控关键指标:
// - 数据分布均衡度
// - chunk迁移频率
// - 查询性能
// - 分片间网络延迟
// 电商系统分片设计
// 订单表按用户ID哈希分片
sh.enableSharding("ecommerce")
db.orders.createIndex({ userId: "hashed" })
sh.shardCollection("ecommerce.orders", { userId: "hashed" })
// 商品表按分类和商品ID复合分片
db.products.createIndex({ category: 1, productId: 1 })
sh.shardCollection("ecommerce.products", { category: 1, productId: 1 })
// 日志表按时间范围分片(配合TTL索引)
db.logs.createIndex({ timestamp: 1 })
sh.shardCollection("ecommerce.logs", { timestamp: 1 })
db.logs.createIndex({ timestamp: 1 }, { expireAfterSeconds: 2592000 }) // 30天