15:mongodb副本集和文档管理
概述
必须连接mongos的操作
业务读写:增删改查、创建集合、索引操作、事务执行
集群管理:添加 / 移除分片、启用数据库分片、设置分片键
数据平衡:查看 / 调整分片平衡器状态
权限管理:创建用户、分配角色
监控查询:sh.status()、db.stats()、explain() 分析执行计划
需要连接 Config Server 的操作
元数据紧急修复:当元数据损坏、无法通过 Mongos 恢复时
集群诊断:排查元数据一致性问题
版本升级:仅在官方指导的升级流程中,对 Config Server 单独操作
备份恢复:对 Config Server 副本集单独执行备份 / 恢复(需暂停集群写入)
需要连接 Shard 的操作
数据紧急修复:单个 Shard 副本集故障时,直接连接修复(如重新选举主节点)
分片级维护:扩容 Shard 副本集节点、升级 Shard 版本
数据迁移:手动迁移块(仅在平衡器自动迁移失败时)
性能诊断:排查单个 Shard 的慢查询、资源瓶颈
数据库相关命令
- 数据库名称规范
- 不能是空字符串(“”)
- 不得含有‘’、空格、.、$、/、\和\0(空字符)
- 应全部小写
- 最多64字节
show dbs # 查看有哪些数据库(和大小)
db # 查看当前正在使用的数据库
use DATABASE_NAME # 切换到目标数据库(不存在则后续写入时自动创建)
db.mydb.insertOne({name: "test"}) # 写入一条数据触发创建
db.dropDatabase() # 删除当前数据库
集合相关命令
-
集合有两种:
- 动态集合(标准集合、普通集合):无大小限制、支持增删改查所有操作。适用大多数业务数据存储(如用户信息、订单等)
- 固定集合:其大小固定。当达到设置的大小时,最老的数据将被删除,最新的数据被添加到末端。必须使用createCollection函数显式地创建。
-
集合命名规范
- 必须以字母或下划线(_)开头,名字中可以有数字。
- 不能使用$符号(符号是MongoDB的保留关键字),不能使用空白字符串(" "),不能使用null字符,也不能以system."字符串开头。
- 最大长度为128字符,但建议使用简短的集合名称(大约不超过9个字符)。
集合的基础命令
show collections // 显示当前数据库所有集合
db.COLLECTION_NAME.drop() // 删除集合
// 显式创建固定集合(size 单位:字节)
// capped:表示固定集合;size最大字节数;max最多文档数,超过会覆盖
db.createCollection("logs", {capped: true,size: 1000000,max: 1000});
db.users.insertOne({ name: "张三" }) // 隐式创建动态集合(插入数据时自动生成)
db.createCollection("products") // 显式创建动态集合(不带 capped 参数)
向集合插入单个文档 insertOne()
db.collection.insertOne(
{
文档在这写
},
{
writeConcern: 写关注策略 // 可选,下面有解释
}
)
- 示例
# 向 users 集合插入一条数据
db.users.insertOne(
{
name: "张三",
age: 25,
email: "zhangsan@example.com",
hobbies: ["篮球", "编程"]
},
{
writeConcern: {w: 1}
}
)
- 返回结果
# insertedId 是自动生成的。也可以手动指定 "_id"
{
"acknowledged": true,
"insertedId": ObjectId("5f8d8a7b9d6c4e3d12345678")
}
向集合插入多个文档 insertMany()
db.collection.insertMany(
[
{文档1},
{文档2},
],
{
writeConcern: 写关注策略,
ordered: <boolean> # 是否按顺序插入(默认 true)
}
)
- 示例
# 向 users 集合插入多条数据
db.users.insertMany(
[
{ name: "李四", age: 30, city: "北京" },
{ name: "王五", age: 28, email: "wangwu@example.com" }
],
{
writeConcern: { w: 1 },
ordered: true
}
)
- 返回结果
{
"acknowledged": true,
"insertedIds": [
ObjectId("5f8d8a7b9d6c4e3d12345679"),
ObjectId("5f8d8a7b9d6c4e3d1234567a")
]
}
插入有多层嵌套关系的文档
db.users.insertOne({
name: "赵六",
age: 35,
gender: "男",
address: {
province: "广东省",
city: "深圳市",
district: "南山区",
detail: "科技园路100号",
contact: {
phone: "13800138000",
postcode: "518000"
}
},
hobbies: ["爬山", "摄影", "烹饪"],
orders: [
{
orderId: "ORD20260121001",
createTime: new Date(),
amount: 299.99,
status: "已支付",
products: [
{ productId: "PROD001", name: "MongoDB实战教程", price: 89.99, num: 1 },
{ productId: "PROD002", name: "编程鼠标", price: 209.99, num: 1 }
]
},
{
orderId: "ORD20260121002",
createTime: new Date("2026-01-20"),
amount: 159.00,
status: "已发货",
products: [
{ productId: "PROD003", name: "机械键盘", price: 159.00, num: 1 }
]
}
]
})
示例:插入有多层嵌套关系的文档
// 插入包含嵌套结构的用户数据
db.users.insertOne({
// 基础字段
name: "赵六",
age: 35,
gender: "男",
// 第一层嵌套:地址(嵌套文档)
address: {
province: "广东省",
city: "深圳市",
district: "南山区",
detail: "科技园路100号",
// 地址内再嵌套:联系方式
contact: {
phone: "13800138000",
postcode: "518000"
}
},
// 嵌套数组:多个爱好(简单数组)
hobbies: ["爬山", "摄影", "烹饪"],
// 多层嵌套:订单列表(数组 + 嵌套文档)
orders: [
{
orderId: "ORD20260121001",
createTime: new Date(), // 动态生成当前时间
amount: 299.99,
status: "已支付",
// 订单内嵌套:商品列表
products: [
{ productId: "PROD001", name: "MongoDB实战教程", price: 89.99, num: 1 },
{ productId: "PROD002", name: "编程鼠标", price: 209.99, num: 1 }
]
},
{
orderId: "ORD20260121002",
createTime: new Date("2026-01-20"),
amount: 159.00,
status: "已发货",
products: [
{ productId: "PROD003", name: "机械键盘", price: 159.00, num: 1 }
]
}
]
})
查询集合中的文档
// 查询集合中的所有文档
db.users.find() 或 db.users.find().pretty()
// 查询users集合中包含键值对name: "tom"的文档
db.users.find({name: "tom"})
// 只查看第一个文档
db.users.findOne()
使用点号查询有嵌套关系的文档
db.users.find({"orders.amount": 299.99})
条件查询
- MongoDB与RDBMS的条件操作比较
![image]()
// 查找年龄小于25的
db.users.find({"age":{$lt:25}})
// 查找年龄大于25,小于30的
db.users.find({"age":{$gt:25},"age":{$lt:30}})
- in、all
// 查找爱好是爬山或摄影的
db.users.find({"hobbies":{$in:['爬山', '摄影']}})
// 查找爱好不是爬山或摄影的
db.users.find({"hobbies":{$in:['爬山', '摄影']}})
// 查找爱好是爬山和摄影的(有其他爱好的也会被查到)
db.users.find({"hobbies":{$all:['爬山', '摄影']}})
- or、and、not
- not只能连接正则或条件表达式,不能直接跟字符串或数值等字面量
// 查找名字是tom或者年龄大于30的
db.users.find({$or:[{name: "tom"},{age: {$gt: 30}}]})
// 查找名字是tom并且城市是北京的
db.users.find({$and:[{name: "tom"},{city: "Beijing"}]})
// 查询年龄不大于30的用户(等价于 age <= 30)
db.users.find({ age: { $not: { $gt: 30 } } })
- 对列表切片 slice
// 这里使用另一个例子,新加入两个文档
db.food.insertOne({fruit: ["苹果", "香蕉", "鸭梨", "西瓜", "芒果" ]})
db.food.insertOne({fruit: ["樱桃", "香蕉", "苹果", "葡萄", "草莓"]})
// slice只能出现在find()的第二个参数(投影参数) 中,用于指定 “显示数组的前N 条/后 N 条”;
db.food.find(
{}, // 第一个参数:查询条件(空表示查询所有文档)
{ fruit: { $slice: 2 } } // 第二个参数:投影,只显示 fruit 数组前2个元素
)
// 只显示后两个元素
db.food.find({}, {fruit: {$slice: -2}})
// 从第二个元素(索引从0计算)开始,包含第二个元素,共展示两个元素
db.food.find({}, {fruit: {$slice: 1, 2}})
- 按文档元素数量查询 size
// 查找有3个元素的文档
db.food.find({fruit: {$size: 3}})
- 按字段是否存在查询 exists
// 查找有hobbies字段的文档
db.users.find({hobbies: {$exists: true}})
// 查找没有address字段的文档
db.users.find({address: {$exists: false}})
- limit、skip、sort
// 显示匹配查询条件的前三个文档
db.food.find().limit(3)
// 跳过匹配查询条件的前2个文档,显示后续文档
db.food.find().skip(2)
// 查询结果按年龄升序排列
db.users.find().sort({age: 1})
- 查询结果显示指定字段
// 查询结果只显示age字段(id字段默认显示)
db.users.find({}, {age: 1}).sort({age: 1})
// 查询结果只显示age字段,id字段不显示
db.users.find({}, {age: 1, _id: 0}).sort({age: 1})
// 不能同时混合使用 0 和 1(除了 _id 字段),比如不能既写 name:1 又写 age:0(会报错)。
- 查询集合中文档的数量
// 查询food集合中文档的数量
db.food.find().count()
- 正则表达式
- option选项有i、m、x、s
// 插入测试数据
db.users.insertOne({ name: "ZhangSan", age: 28 })
db.users.insertOne({ name: "zhangsan", age: 23 })
// 查找以zhang开头的人,忽略大小写
db.users.find({name: {$regex: /^zhang/, $options: 'i'}})
更新集合中第一个匹配文档 updateOne
- updateOne 会接受(至少)两个参数:第一个用于查找要更新文档的限定条件,第二个用于描述要进行更新的文档。
# 查找name为李四的文档,新增键sex
db.users.update({name: "李四"},{$set: {sex: "man"}})
# $set运算符还表示修改,修改年龄为20
db.users.update({name: "李四"},{$set: {age: 20}})
更新集合中所有匹配文档 updateMany
db.users.updateMany({name: "张三"},{$set: {sex: "man"}})
删除集合中匹配的第一个文档 deleteOne
db.users.deleteOne({name: "张三",sex: "man"})
删除集合中所有匹配的文档 deleteMany
db.users.deleteMany({name: "张三",sex: "man"})
查看集合users内文档个数
db.users.countDocuments()
用户与权限管理相关命令
创建用户
// 创建管理员用户
use admin
db.createUser({
user: "admin",
pwd: "password123",
roles: ["root"]
})
// 创建普通用户(对特定集合)
use test // 先切换到要创建用户的集合
db.createUser({
user: "user2",
pwd: "user123",
roles: [
{role:"readWrite",db:"test1"} // 用户的角色和作用数据库
]
})
查看用户
use test // 切换库
show users // 查看当前数据库有哪些用户
db.getUser("user1") // 查看user1用户的信息
// 查看当前数据库的所有用户的信息
use admin
db.getUsers()
// 查看所有数据库的所有用户信息
use admin
db.system.users.find()
修改用户
// 新增用户user1对test1库的read角色权限
db.grantRolesToUser(
"user1",
[ { role: "read", db: "test1" } ]
);
// 移除用户user1对test1库的read角色权限
db.revokeRolesFromUser(
"user1",
[ { role: "read", db: "test1" } ]
);
删除用户
db.dropUser("user1") // 删除用户
// 验证用户user1的密码,会提示输入密码
db.auth("user1")
// 明文方式修改user1用户的密码
db.changeUserPassword("user1",123456)
// 密文方式修改user1用户的密码
db.changeUserPassword("user1",passwordPrompt())
数据备份与恢复
备份config server副本集
# uri中指定副本的主机名、端口、副本集名称
mongodump \
--uri "mongodb://mongo01:27020,mongo02:27020,mongo03:27020/?replicaSet=configserver" \
--out /backup/mongodb/config_server_$(date +%Y%m%d_%H%M%S) \
--gzip
- 查看备份
![image]()
备份分片副本集
# 备份shard1副本集
mongodump \
--uri "mongodb://mongo01:27017,mongo02:27017,mongo03:27017/?replicaSet=rs1" \
--out /backup/mongodb/shard1_$(date +%Y%m%d_%H%M%S) \
--gzip
# 备份shard2副本集
mongodump \
--uri "mongodb://mongo01:27018,mongo02:27018,mongo03:27018/?replicaSet=rs2" \
--out /backup/mongodb/shard2_$(date +%Y%m%d_%H%M%S) \
--gzip
# 备份shard3副本集
mongodump \
--uri "mongodb://mongo01:27019,mongo02:27019,mongo03:27019/?replicaSet=rs3" \
--out /backup/mongodb/shard3_$(date +%Y%m%d_%H%M%S) \
--gzip
- 查看备份
![image]()
备份单个数据库
备份config server的单个数据库
mongodump \
--uri "mongodb://mongo01:27020,mongo02:27020,mongo03:27020/?replicaSet=configserver" \
--db local \
--out /backup/mongodb/config_single_db_$(date +%Y%m%d_%H%M%S) \
--gzip
- 查看备份
![image]()
备份shard的单个数据库
mongodump \
--uri "mongodb://mongo01:27018,mongo02:27018,mongo03:27018/?replicaSet=rs2" \
--db test \
--out /backup/mongodb/shard2_single_db_$(date +%Y%m%d_%H%M%S) \
--gzip
- 查看备份
![image]()
备份单个集合
备份config server的单个集合
# 连接到config server主节点。查看数据库,查看集合
mongosh --host mongo01:27020
- 查看要备份的数据库和集合
![image]()
# 备份
mongodump \
--uri "mongodb://mongo01:27020,mongo02:27020,mongo03:27020/?replicaSet=configserver" \
--db local \
--collection oplog.rs \
--out /backup/mongodb/config_single_col_$(date +%Y%m%d_%H%M%S) \
--gzip
- 查看备份
![image]()
备份shard2的单个集合
mongodump \
--uri "mongodb://mongo01:27018,mongo02:27018,mongo03:27018/?replicaSet=rs2" \
--db test \
--collection users \
--out /backup/mongodb/shard2_single_col_$(date +%Y%m%d_%H%M%S) \
--gzip
- 查看备份
![image]()
恢复单个数据库
- --uri指定mongos的IP地址和端口
- 如果备份时有--gzip选项,恢复时也要加上
# 恢复test数据库
mongorestore \
--uri "mongodb://mongos01:27021" \
--gzip \
--db test \
/backup/mongodb/shard2_single_db_20260122_063349/test
导入或导出MongoDB的数据
- mongoDB不支持导出单个数据库
导出单个集合
- --uri指定mongos的IP地址和端口
# 导出test库的users集合为json格式
mongoexport \
--uri "mongodb://mongo01:27021/test" \
--collection users \
--out /backup/mongodb/user_export_$(date +%Y%m%d_%H%M%S).json \
--pretty
# 导出test库的users集合为csv格式,只导出name、age字段
mongoexport \
--uri "mongodb://mongo01:27021/test" \
--collection users \
--type csv \
--fields name,age \
--out /backup/mongodb/user_export_$(date +%Y%m%d_%H%M%S).csv
# 有条件导出,导出年龄小于30的
mongoexport \
--uri "mongodb://mongo01:27021/test" \
--collection users \
--query '{"age": {"$lt": 30}}' \
--out /backup/mongodb/user_export_$(date +%Y%m%d_%H%M%S).json \
--pretty
导入JSON到集合
- 如果json文件最外层没有中括号,例如{"name":"a"}\n{"name":"b"}不加--jsonArray选项。否则加上--jsonArray选项
- 新数据库会自动创建
mongoimport \
--uri "mongodb://mongo01:27021/test1" \
--collection users\
--file user_adult_20260122_081322.json \
--jsonArray \
副本集相关命令
添加从节点
//添加从节点,并指定特定的属性
rs.add(
{
_id: <int>,
host: <string>, #主机名称(必须)
arbiterOnly: <boolean>, #是否为仲裁节点
buildIndexes: <boolean>, #是否要创建索引
hidden: <boolean>, #是否隐藏
priority: <number>, #优先级
tags: <document>, #设置读偏好的标签
secondaryDelaySecs: <int>, #比主节点延迟多少秒
votes: <number> #是否参与投票,有效值是0或1
},
arbiterOnly: <boolean> #是否为仲裁节点
)
//添加仲裁节点,等价于rs.add(<host>, true)
rs.addArb(host)
添加仲裁节点
rs.addArb("mongo03:27012")
查看当前副本集的配置
rs.conf
冻结节点
// 冻结指定的节点,在指定的时间内不参与竞选主节点(单位秒)
rs.freeze(100)
启动副本集
rs.initiate(
{
_id : "rs1",
members: [
{ _id : 0, host : "mongo01:27017" },
{ _id : 1, host : "mongo02:27017" },
{ _id : 2, host : "mongo03:27017" }
]
}
)
查看oplog的关键信息
- 连接mongos,使用show databases是看不到local数据库的
- local是 MongoDB 每个节点本地专属的数据库,不会在副本集/分片集群中同步
- 这个命令会查看本地local数据库
- 如果没有local数据库会提示 "MongoServerError[IllegalOperation]: Can't use 'local' database through mongos"
// 连接到副本集的任意节点
mongosh --host mongo03:27018
rs.printReplicationInfo()

重新配置副本集信息
// 语法
rs.reconfig(
<configuration>, // 新配置
{
"force" : <boolean>, // 强制使用新配置,可能会有问题,默认false
"maxTimeMS" : <int> // reconfig命令的最大执行时间,单位毫秒,默认无限期
}
)
查看副本集状态
- 从副本集的从节点成员的角度打印副本集状态的格式化报告
// 连接到任意从节点
mongosh --host mongo03:27018
rs.printSecondaryReplicationInfo()

删除节点
- 建议先停止要删除的副本,这里停止的是mongo03主机上的shard2进程
// 连接shard2所在的分片主节点
mongosh --host mongo02:27018
// 删除节点
rs.remove("mongo03:27018")
查看当前节点所在的副本集状态
- 从该方法运行所在节点的角度返回副本集状态
rs.status()
rs.stepDown
rs.syncFrom
常用运维操作
-
在从节点查数据??
db.getMongo().setSlaveOk() -
查看大于规定时间的sql语句
db.currentOp({"secs_running":{"$gt":50}}) -
查看当前链接情况
currOp = db.currentOp();for (oper in currOp.inprog) { op = currOp.inprog[oper-0]; if (op.op != "none") {print("connect opId: " + op.opid + " running secs: "+op.secs_running + " client info: "+ op.client + " ns info: "+op.ns + " query command: "+ op.query); } };
查看慢查询级别和时间
- was:慢查询分析器的运行级别(0 = 关闭,1 = 仅记录慢查询,2 = 记录所有操作)
- slowms:是慢查询阈值,单位ms
- sampleRate:慢查询的采样率(0~1 之间,1=100% 采样,0.5=50% 采样)
db.getProfilingStatus()

开启慢查询
db.setProfilingLevel(1, 200)
-
查看关于某个集合的慢查询
db.system.profile.find({ns:'mydb.mycollection'}) / / 将mydb替换成实际库名,将mycollection替换成实际集合名 -
杀慢查询
currOp = db.currentOp(); for (oper in currOp.inprog) { op = currOp.inprog[oper-0]; if (op.op == "query" && op.secs_running >= 5) { print("Killing opId: " + op.opid + " running for over secs: " + op.secs_running); db.killOp(op.opid); } }; -
查看建索引进度
db.currentOp({"ns": "mydb.$cmd"})
查看mongodb每个集合的读写数据量
- 仅统计有读写操作的集合(无访问的集合不会出现在结果中);
- 默认按 ns(命名空间,格式 数据库.集合)展示,仅显示采集周期内有流量的集合;
- 若连接的是副本集从节点,默认仅统计从节点自身的操作(如同步操作),而非主节点的业务读写。
mongotop --host mongo01:27017

查看mongodb当前监控指标
mongostat --host mongo01:27017

- 性能监控
db.stats() // 查看数据库状态
db.users.stats() // 查看集合状态
db.currentOp() // 查看当前操作
db.killOp(opid) // 终止操作(使用opid)
- 索引管理
db.users.createIndex({ name: 1 }) // 创建索引
db.users.createIndex({ name: 1, age: -1 }) // 创建复合索引
db.users.getIndexes() // 查看索引
db.users.dropIndex("name_1") // 删除索引
- 数据验证与修复
db.users.validate() // 验证集合完整性
use mydb
db.repairDatabase() // 修复数据库
- 连接管理
db.adminCommand({ currentOp: true, $all: true }) // 查看当前连接
db.adminCommand({ setParameter: 1, maxConnectionIdleTime: 300 }) // 设置连接超时(秒)
- 日志管理
# 动态设置日志级别
db.adminCommand({ setParameter: 1, logLevel: 2 }) # 1-5(1最简,5最详细)
- 内存管理
db.serverStatus().mem // 查看内存使用
// 设置缓存大小(MB)
db.adminCommand({ setParameter: 1, wiredTigerEngineRuntimeConfig: "cache_size=1G" })
- 安全加固
// 启用认证后连接
mongosh -u admin -p password123 --authenticationDatabase admin
// 禁用危险命令
db.adminCommand({ setParameter: 1, disableJavaScriptJIT: true })
读偏好(Read Preference)
- 读偏好决定了 MongoDB 客户端如何将读请求路由到副本集的成员。它主要用于控制读取操作的分布和一致性。
主要读偏好模式
- primary(默认)- 所有读操作都从主节点读取
- primaryPreferred - 优先从主节点读取,如果主节点不可用则从从节点读取
- secondary- 所有读操作都从从节点读取
- secondaryPreferred - 优先从从节点读取,如果没有可用的从节点则从主节点读取
- nearest - 从网络延迟最低的节点读取(无论是主节点还是从节点)
设置读偏好
# 在连接字符串中设置:
mongodb://host1,host2,host3/?readPreference=secondaryPreferred
# 在查询时设置:
db.collection.find().readPref('secondary')
Read Concern(读取关注)
- 控制读取数据的 一致性级别,决定是否可能读到“脏数据”或“回滚的数据”
读关注模式
- local(默认) - 读取节点最新的数据(可能未持久化或回滚)。适合高性能读取,不严格要求一致性
- available - 类似 local,但分片集群中可能跳过孤儿文档过滤。适合特殊用途(如迁移期间)
- majority - 只返回已写入大多数节点的数据(避免回滚)。适合金融、订单等关键业务
- linearizable - 强一致性,确保读取最新已确认的数据(性能较低)。适合严格一致性要求(如选举系统)
- snapshot - 多文档事务中,读取事务开始时的快照数据
写关注(Write Concern)
- 写关注决定了MongoDB在向客户端确认写操作成功之前必须满足的条件。它控制着写入的持久性和确认级别。
主要写关注选项
-
w: 1(默认)
- 只需要主节点确认写入
- 提供基本持久性
- 示例:
-
w: "majority"
- 需要大多数副本集成员确认写入
- 提供更强的持久性保证
- 示例:
-
w: N(数字)
- 需要N个节点确认写入(包括主节点)
- 示例:{ w: 2 }(需要主节点+1个节点确认)
-
j: true(journal)
- 要求写入操作写入到磁盘日志后才确认
- 提供更强的持久性
- 示例:
-
wtimeout
- 设置写操作超时时间(毫秒)
- 示例:
设置写关注
# 在连接字符串中设置
mongodb://host1,host2,host3/?w=majority&journal=true
# 在操作时设置
db.collection.insertOne(
{"_id": 10, "name": tom, "age": 10},
{ writeConcern: { w: 'majority' } })









浙公网安备 33010602011771号