这是《MongoDB 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 本速查以 MongoDB 7.x / 8.x 通用用法为主。不同版本命令、参数和默认值可能变化,生产操作前以当前版本文档为准。
核心概念
| 概念 | 说明 |
|---|---|
| Database | 数据库 |
| Collection | 集合 |
| Document | BSON 文档 |
| Field | 字段 |
_id |
文档唯一标识 |
| Replica Set | 副本集 |
| Primary | 接收写入 |
| Secondary | 同步副本 |
| Oplog | 复制日志 |
| Shard | 数据分片 |
| mongos | 分片路由 |
| Config Server | 分片元数据 |
| WiredTiger | 默认存储引擎 |
常用端口
| 组件 | 默认端口 |
|---|---|
| mongod / mongos | 27017 |
生产环境只监听内网地址,不应暴露公网。
连接
单节点:
mongodb://user:password@mongo1:27017/shop?authSource=admin
副本集:
mongodb://user:password@mongo1:27017,mongo2:27017,mongo3:27017/shop?replicaSet=rs0&authSource=admin
分片集群:
mongodb://user:password@mongos1:27017,mongos2:27017/shop?authSource=admin
Docker 快速启动
docker run -d \
--name mongo \
-p 27017:27017 \
-e MONGO_INITDB_ROOT_USERNAME=admin \
-e MONGO_INITDB_ROOT_PASSWORD=secret123 \
-v mongo-data:/data/db \
mongo:7.0
进入 shell:
docker exec -it mongo mongosh -u admin -p secret123
数据库与集合
show dbs
use shop
show collections
创建集合:
db.createCollection("orders")
删除集合:
db.orders.drop()
查看统计:
db.orders.stats()
插入
db.orders.insertOne({
_id: "o_1",
user_id: "u_1",
status: "CREATED",
amount: NumberDecimal("100.00")
})
db.orders.insertMany([
{ _id: "o_2", user_id: "u_1", status: "CREATED" },
{ _id: "o_3", user_id: "u_2", status: "CREATED" }
], { ordered: false })
查询
db.orders.findOne({ _id: "o_1" })
db.orders.find({
user_id: "u_1",
status: { $in: ["CREATED", "PAID"] }
})
db.orders.find(
{ user_id: "u_1" },
{ status: 1, amount: 1, created_at: 1 }
).sort({ created_at: -1 }).limit(20)
更新
db.orders.updateOne(
{ _id: "o_1", status: "WAIT_PAY" },
{
$set: { status: "PAID", paid_at: new Date() },
$inc: { version: 1 }
}
)
常用操作符:
| 操作符 | 说明 |
|---|---|
$set |
设置字段 |
$unset |
删除字段 |
$inc |
增减 |
$push |
数组追加 |
$pull |
数组删除 |
$addToSet |
去重追加 |
$currentDate |
更新时间 |
删除
db.orders.deleteOne({ _id: "o_1" })
db.orders.deleteMany({ status: "CLOSED", created_at: { $lt: cutoff } })
索引
db.orders.createIndex({ user_id: 1, created_at: -1 })
db.orders.createIndex(
{ order_no: 1 },
{ unique: true }
)
db.sessions.createIndex(
{ expires_at: 1 },
{ expireAfterSeconds: 0 }
)
db.orders.getIndexes()
db.orders.dropIndex("user_id_1_created_at_-1")
执行计划
db.orders.find({ user_id: "u_1" }).explain("executionStats")
重点:
winningPlan
IXSCAN / COLLSCAN
keysExamined
docsExamined
nReturned
SORT
聚合
db.orders.aggregate([
{ $match: { status: "PAID" } },
{ $unwind: "$items" },
{
$group: {
_id: "$items.sku_id",
quantity: { $sum: "$items.quantity" },
amount: { $sum: "$items.price" }
}
},
{ $sort: { quantity: -1 } },
{ $limit: 20 }
])
事务
const session = db.getMongo().startSession();
try {
session.startTransaction({
readConcern: { level: "snapshot" },
writeConcern: { w: "majority" }
});
session.getDatabase("shop").orders.updateOne(
{ _id: "o_1", status: "WAIT_PAY" },
{ $set: { status: "PAID" } }
);
session.commitTransaction();
} catch (e) {
session.abortTransaction();
} finally {
session.endSession();
}
副本集
rs.status()
rs.conf()
db.hello()
rs.printSecondaryReplicationInfo()
db.printReplicationInfo()
主动切换:
rs.stepDown(120)
分片
sh.status()
sh.enableSharding("shop")
sh.shardCollection("shop.orders", { tenant_id: 1, user_id: 1 })
sh.isBalancerRunning()
sh.stopBalancer()
sh.startBalancer()
Change Stream
const stream = db.orders.watch([], {
fullDocument: "updateLookup"
});
const event = stream.next();
db.sync_tokens.updateOne(
{ name: "orders" },
{ $set: { token: event._id, updated_at: new Date() } },
{ upsert: true }
)
读写关注
db.orders.insertOne(
{ _id: "o_1", status: "CREATED" },
{ writeConcern: { w: "majority", j: true, wtimeout: 3000 } }
)
db.orders.find({ _id: "o_1" }).readPref("primary")
监控
db.serverStatus()
db.currentOp({ active: true, secs_running: { $gte: 60 } })
关键指标:
connections_current
opcounters
query_latency
cache_bytes
cache_dirty_bytes
pages_read_into_cache
replication_lag_seconds
oplog_window
disk_usage
transactions_write_conflicts
备份恢复
mongodump --uri="mongodb://backup:pass@mongo1,mongo2,mongo3/shop?replicaSet=rs0" --gzip --out=/backup
mongorestore --uri="mongodb://admin:pass@target:27017/admin" --gzip --drop /backup
大规模生产环境优先使用快照或平台备份,并定期恢复演练。
安全检查
- 启用访问控制;
- 禁止公网暴露;
- 按服务创建最小权限用户;
- 启用 TLS;
- 敏感字段脱敏;
- 审计高危操作;
- 密钥进入 KMS 或 Secret;
- 定期复核账号权限。
常见错误
| 错误 | 排查 |
|---|---|
| Connection refused | 进程、端口、bindIp |
| Authentication failed | 密码或 authSource |
| Not primary | 写到 Secondary 或选举中 |
| Duplicate key | 唯一索引冲突 |
| Query exceeded memory | 排序或聚合无索引 |
| WriteConflict | 并发更新同一文档 |
| Not primary and secondary ok | 拓扑状态或读偏好 |
生产上线清单
- 副本集至少三数据节点;
- 跨故障域部署;
- 认证和 TLS 开启;
- 业务用户最小权限;
- 高频查询有索引;
- 文档和数组大小可控;
- 一致性矩阵明确;
- 备份和恢复演练完成;
- 监控告警接入;
- 容量模型和扩容预案完成;
- 分片键经过评审;
- 升级回滚方案明确。
思考题
- 你的核心集合使用什么读写关注组合?
- 最近一次慢查询 Top 10 是否都验证过 explain?
- 副本集 oplog 窗口能覆盖多长故障?
- 备份能否恢复用户、索引和分片元数据?
- 工作集是否已经接近内存上限?