1. MongoDB 基础
MongoDB 以其灵活的文档模型和强大的查询能力,成为现代应用开发的热门选择。本章将为你提供一份即查即用的 CRUD(增删改查)操作速查表,涵盖基础语法、特殊场景,并附上 Java 8 驱动代码示例,让你快速上手 MongoDB 的核心数据操作。
1.1 CRUD 基础操作速查表
//插入
db.users.insertOne({name: "张三", age: 25, city: "北京"}) //插入单条文档
db.users.insertMany([{name: "李四"}, {name: "王五"}]) //批量插入多条文档
//查询
db.users.find() //查询集合中所有文档
db.users.find({age: {$gt: 20}}) //查询 age > 20 的文档
db.users.find({city: "北京"}, {name: 1, age: 1}) //只返回 name 和 age 字段(投影)
db.users.findOne({name: "张三"}) //返回第一条匹配的文档
//更新
db.users.updateOne({name: "张三"}, {$set: {age: 26}}) //更新第一条匹配文档的 age 字段
db.users.updateMany({city: "北京"}, {$inc: {age: 1}}) //将所有北京用户的 age 加 1
db.users.replaceOne({name: "张三"}, {name: "张三", age: 27, city: "上海"}) //完全替换文档(保留 _id)
//删除
db.users.deleteOne({name: "李四"}) //删除第一条匹配的文档
db.users.deleteMany({age: {$lt: 18}}) //删除所有 age < 18 的文档
//索引
db.users.createIndex({city: 1}, {name: "索引名称(可不填)"}) //在 city 字段上创建升序索引
db.users.createIndex({name: 1, age: -1}) //创建复合索引
db.users.getIndexes() //查看集合所有索引
1.2 特殊使用场景
1.2.1 聚合查询(Aggregation)
聚合管道是 MongoDB 最强大的数据分析功能,可以完成复杂的数据转换和统计。
// 按城市分组,计算每个城市的平均年龄和人数
db.users.aggregate([
{ $match: { age: { $gte: 18 } } }, // 筛选成年用户
{ $group: {
_id: "$city",
avgAge: { $avg: "$age" },
count: { $sum: 1 }
}
},
{ $sort: { avgAge: -1 } }, // 按平均年龄降序
{ $limit: 10 } // 只取前10
])
1.2.2 文本搜索(Text Search)
MongoDB 支持全文索引,可用于模糊搜索场景。
// 1. 创建文本索引
db.articles.createIndex({ title: "text", content: "text" })
// 2. 执行文本搜索
db.articles.find({
$text: { $search: "MongoDB 安装教程" }
}).sort({ score: { $meta: "textScore" } })
1.2.3 地理空间查询(Geospatial)
适用于位置相关的应用,如附近的人、附近的商家。
// 1. 创建2dsphere索引(存储经纬度)
db.places.createIndex({ location: "2dsphere" })
// 2. 查询距离某点5公里内的地点
db.places.find({
location: {
$near: {
$geometry: { type: "Point", coordinates: [116.397, 39.908] },
$maxDistance: 5000 // 5公里
}
}
})
1.2.4 数组操作
MongoDB 对数组字段有丰富的查询和更新操作符。
// 查询 tags 包含 "数据库" 的文档
db.articles.find({ tags: "数据库" })
// 查询 tags 同时包含 "MongoDB" 和 "NoSQL" 的文档
db.articles.find({ tags: { $all: ["MongoDB", "NoSQL"] } })
// 向 tags 数组添加新元素(如果不存在)
db.articles.updateOne(
{ _id: ObjectId("...") },
{ $addToSet: { tags: "教程" } }
)
// 从 tags 数组中移除元素
db.articles.updateOne(
{ _id: ObjectId("...") },
{ $pull: { tags: "旧标签" } }
)
1.2.5 事务支持(4.0+)
MongoDB 4.0 开始支持多文档事务,适用于需要强一致性的场景。
// 开启一个会话和事务
const session = db.getMongo().startSession()
session.startTransaction()
try {
const users = session.getDatabase("test").users
const orders = session.getDatabase("test").orders
// 在事务中执行多个操作
users.insertOne({ name: "用户A", balance: 100 })
orders.insertOne({ userId: "用户A", amount: 50 })
session.commitTransaction()
console.log("事务提交成功")
} catch (error) {
session.abortTransaction()
console.log("事务回滚:", error)
} finally {
session.endSession()
}
1.3 Java 8 中使用 MongoDB
1.3.1 环境准备
在 pom.xml 中添加 MongoDB Java 驱动依赖:
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver-sync</artifactId>
<version>4.11.0</version>
</dependency>
1.3.2 连接 MongoDB
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.MongoCollection;
import org.bson.Document;
public class MongoJavaExample {
public static void main(String[] args) {
// 连接字符串(单机)
String connectionString = "mongodb://localhost:27017";
// 副本集连接
// String connectionString = "mongodb://host1:27017,host2:27017,host3:27017/?replicaSet=rs0";
try (MongoClient mongoClient = MongoClients.create(connectionString)) {
MongoDatabase database = mongoClient.getDatabase("test");
MongoCollection<Document> collection = database.getCollection("users");
System.out.println("连接成功,数据库: " + database.getName());
// 执行CRUD操作...
}
}
}
1.3.3 Java 8 中的 CRUD 操作
插入文档:
// 插入单条
Document user = new Document("name", "张三")
.append("age", 25)
.append("city", "北京")
.append("hobbies", Arrays.asList("读书", "游泳"));
collection.insertOne(user);
// 批量插入
List<Document> users = Arrays.asList(
new Document("name", "李四").append("age", 30),
new Document("name", "王五").append("age", 28)
);
collection.insertMany(users);
查询文档:
// 查询所有
collection.find().forEach(document -> System.out.println(document.toJson()));
// 条件查询
Document query = new Document("age", new Document("$gt", 20));
collection.find(query).forEach(doc -> System.out.println(doc.getString("name")));
// 投影查询(只返回指定字段)
Document projection = new Document("name", 1).append("_id", 0);
collection.find().projection(projection)
.forEach(doc -> System.out.println(doc.toJson()));
// 排序和分页
collection.find()
.sort(new Document("age", -1)) // 按年龄降序
.skip(0) // 跳过前0条
.limit(10) // 限制10条
.forEach(doc -> System.out.println(doc.toJson()));
更新文档:
// 更新单条
Document filter = new Document("name", "张三");
Document update = new Document("$set", new Document("age", 26));
collection.updateOne(filter, update);
// 更新多条(年龄加1)
Document filterAll = new Document("city", "北京");
Document increment = new Document("$inc", new Document("age", 1));
collection.updateMany(filterAll, increment);
// 使用更新选项(upsert:不存在则插入)
UpdateOptions options = new UpdateOptions().upsert(true);
collection.updateOne(
new Document("name", "赵六"),
new Document("$set", new Document("age", 35)),
options
);
删除文档:
// 删除单条
collection.deleteOne(new Document("name", "李四"));
// 删除多条
collection.deleteMany(new Document("age", new Document("$lt", 18)));
1.3.4 Java 8 中的聚合查询
import static com.mongodb.client.model.Aggregates.*;
import static com.mongodb.client.model.Filters.*;
import static com.mongodb.client.model.Accumulators.*;
List<Document> pipeline = Arrays.asList(
match(gte("age", 18)), // 筛选成年用户
group("$city", // 按城市分组
avg("avgAge", "$age"),
sum("count", 1)
),
sort(descending("avgAge")), // 按平均年龄降序
limit(10) // 取前10
);
collection.aggregate(pipeline)
.forEach(doc -> System.out.println(doc.toJson()));
1.3.5 Java 8 中的事务处理
try (ClientSession session = mongoClient.startSession()) {
session.startTransaction();
try {
MongoCollection<Document> accounts = database.getCollection("accounts");
MongoCollection<Document> transactions = database.getCollection("transactions");
// 转账操作:从A账户扣款,向B账户加款
accounts.updateOne(
session,
eq("accountId", "A"),
new Document("$inc", new Document("balance", -100))
);
accounts.updateOne(
session,
eq("accountId", "B"),
new Document("$inc", new Document("balance", 100))
);
// 记录交易
transactions.insertOne(session,
new Document("from", "A")
.append("to", "B")
.append("amount", 100)
.append("time", new Date())
);
session.commitTransaction();
System.out.println("转账成功");
} catch (Exception e) {
session.abortTransaction();
System.out.println("转账失败,已回滚: " + e.getMessage());
}
}
1.4 最佳实践与性能提示
- 连接池配置:使用单例模式创建
MongoClient,避免频繁创建连接。 - 批量操作:大量插入/更新时使用
insertMany和bulkWrite。 - 索引优化:为查询条件字段创建索引,复合索引注意字段顺序。
- 投影优化:只查询需要的字段,减少网络传输。
- 游标管理:处理大量数据时使用游标分批获取,避免内存溢出。
// 批量写入示例
List<WriteModel<Document>> writes = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
writes.add(new InsertOneModel<>(
new Document("index", i).append("data", "value" + i)
));
}
collection.bulkWrite(writes);
掌握了这些基础 CRUD 操作和 Java 8 集成方法,你已经可以应对大多数 MongoDB 开发场景。接下来,我们将进入安装部署环节,从单机到集群,一步步搭建生产可用的 MongoDB 环境。
2. 安装篇:从单机到集群
MongoDB 的安装分为两大场景:开发测试常用的单机模式,以及生产必选的副本集与分片集群。考虑到生产环境常有“无法出网”的限制,本章将 在线安装 和 离线安装 分开讲解。
2.1 单机模式安装(以 CentOS 7 为例)
2.1.1 在线安装(有外网)
如果服务器可以直接访问互联网,配置官方 YUM 仓库是最推荐的方式:
# 创建仓库文件
cat > /etc/yum.repos.d/mongodb-org-6.0.repo <<EOF
[mongodb-org-6.0]
name=MongoDB Repository
baseurl=https://repo.mongodb.org/yum/redhat/7Server/mongodb-org/6.0/x86_64/
gpgcheck=1
enabled=1
gpgkey=https://www.mongodb.org/static/pgp/server-6.0.asc
EOF
# 安装完整组件(server + shell + tools)
yum install -y mongodb-org-6.0.15 mongodb-org-server-6.0.15 \
mongodb-org-shell-6.0.15 mongodb-org-tools-6.0.15
# 启动并设为开机自启
systemctl start mongod
systemctl enable mongod
2.1.2 离线安装(无外网 / 内网环境)
很多企业环境严格隔离,服务器禁止访问公网。此时需要提前在有外网的跳板机上下载好 RPM 包,再传入目标机器。
Step 1:在有外网的机器上下载(仅下载,不安装)
# 先配置仓库(同上),然后仅下载不安装
yum install --downloadonly --downloaddir=/tmp/mongo-rpms \
mongodb-org-6.0.15 mongodb-org-server-6.0.15 \
mongodb-org-shell-6.0.15 mongodb-org-tools-6.0.15
小技巧:如果依赖项较多,建议直接使用
repotrack或reposync下载全量依赖包到同一目录,避免传过去之后发现缺依赖。
补充:如果跳板机是 Windows,如何下载 RPM 包?
不少运维同学的“有网机器”其实是自己的 Windows 办公电脑。此时不需要配置 YUM 仓库,直接从 MongoDB 官方下载页面获取即可:
- 打开 MongoDB Community Server Download
- 版本选 6.0.x,平台选 Red Hat / CentOS(或与目标服务器一致的系统),包格式保持 tgz 或直接下载 RPM 包。
- 点击「Download」获取安装包;如果是 tgz 压缩包,解压后可找到所有 RPM 文件。
- 把下载好的 RPM 包通过 WinSCP / Xftp 等工具传入目标服务器,接下来的安装步骤与 Linux 跳板机流程完全一致(
yum localinstall -y *.rpm)。
Step 2:将 RPM 包传入目标服务器
# 用 scp、rsync 或 U 盘将整个 /tmp/mongo-rpms 目录拷贝到目标机器
scp -r /tmp/mongo-rpms root@目标IP:/tmp/mongo-rpms
Step 3:在目标服务器上离线安装
# 进入 RPM 包目录,用 rpm 一次性安装(自动处理本地依赖)
cd /tmp/mongo-rpms
rpm -ivh *.rpm --nodeps --force
# 或继续用 yum 本地安装(推荐,会自动排顺序)
yum localinstall -y *.rpm
Step 4:启动与验证
systemctl start mongod
systemctl enable mongod
mongosh --eval "db.runCommand({ connectionStatus: 1 })"
离线安装核心注意点:
- 操作系统版本必须匹配:在有网机器上必须使用与目标服务器完全相同的 CentOS 版本下载包,否则
glibc等核心依赖版本不一致会导致安装失败。 - 架构一致:x86_64 和 ARM(aarch64)的包不能混用;下载前用
uname -m确认。 - 离线依赖兜底:如果
yum localinstall仍报依赖缺失(比如缺少compat-openssl10),需要额外从 CentOS 安装 ISO 镜像中复制对应基础包一道传入。
2.1.3 常见安装报错与解决
| 报错内容 | 原因 | 解决方案 |
|---|---|---|
Requires: libcrypto.so.10 | CentOS 7 OpenSSL 版本不匹配 | yum install -y compat-openssl10;离线则从 ISO 中提取安装 |
Failed to start mongod.service | 配置文件语法错误 | mongod --config /etc/mongod.conf --validate |
Address already in use | 端口 27017 被占用 | ss -tlnp | grep 27017 确认并更换端口 |
Permission denied 无法启动 | 数据目录权限不正确 | chown -R mongod:mongod /var/lib/mongo |
离线安装后 mongosh: command not found | 未安装 mongosh 或 PATH 未包含 | 确认已安装 mongodb-mongosh 包,或直接用 mongo 连接(老版本) |
2.2 集群模式安装
生产环境绝不能只靠单机。MongoDB 的集群体系由两大核心构成:副本集(Replica Set) 提供高可用,分片集群(Sharded Cluster) 提供水平扩展。集群节点的离线部署思路与单机一致:在有网机器下载好所有 RPM,scp 分发到各节点后统一安装。
2.2.1 副本集(Replica Set)部署
我们以三节点副本集为例(虚拟机或三台物理机):
1. 环境准备(每台机器可提前配置 hosts)
# /etc/hosts
192.168.56.101 mongo-rs01
192.168.56.102 mongo-rs02
192.168.56.103 mongo-rs03
2. 配置文件关键修改(所有节点)
net:
port: 27017
bindIp: 0.0.0.0 # 生产环境按需改为内网 IP
replication:
replSetName: "rs-prod"
3. 初始化副本集(只在 Primary 上执行)
rs.initiate({
_id: "rs-prod",
members: [
{ _id: 0, host: "mongo-rs01:27017", priority: 2 },
{ _id: 1, host: "mongo-rs02:27017", priority: 1 },
{ _id: 2, host: "mongo-rs03:27017", priority: 1 }
]
})
2.2.2 集群部署关键注意点
- host 配置格式:副本集初始化中
host字段必须写可跨节点解析的 hostname 或 IP。不要写localhost,否则从节点永远连不上主节点。 - bindIp:从 MongoDB 3.6 起,
bindIp默认值为localhost,集群模式下必须改为0.0.0.0或者本机内网 IP。 - 防火墙:确保所有节点间
27017端口互通。 - 时钟同步:所有节点必须启用 NTP 服务(
chronyd或ntpd),否则心跳检测会出问题,引发频繁切换主节点。 - 离线部署集群:提前在可联网机器上把各节点所需的 RPM 包全部下载完毕,分别传入后统一执行
yum localinstall -y *.rpm,确保所有节点 MongoDB 小版本号完全一致。
2.2.3 分片集群(Sharded Cluster)部署实战
经过副本集部署,你已经具备了高可用能力。但当单个副本集无法承担海量数据时,就需要引入 分片集群。它允许数据按片键分散到多个 Shard 上,实现近乎线性的水平扩展。
架构核心:一个完整的分片集群由三类角色组成——
- Config Server:存储集群元数据和 chunk 分布(生产环境必须为 3 节点副本集)。
- mongos:路由进程,本身不存储数据,接收客户端请求后根据片键将请求转发到对应 Shard。
- Shard:数据实际存储节点,每个 Shard 本身是一个副本集。
启动顺序铁律:必须先启动所有 Config Server → 再启动 Shard 各节点 → 最后启动 mongos。启动 mongos 之前,Config Server 副本集必须已经完成初始化并被选出了 Primary,否则 mongos 启动后无法对外服务。
1. 组件与节点规划(最小生产拓扑推荐 9 台机器)
| 组件 | 节点数 | 端口 | 说明 |
|---|---|---|---|
| Config Server | 3(副本集) | 27019 | 存储 chunk 元数据和 auth 信息 |
| Shard 01 | 3(副本集) | 27017 | 第一片数据存储,正式上线后可扩容 |
| Shard 02 | 3(副本集) | 27017 | 第二片数据存储 |
| mongos | 2 | 27017 | 无状态路由,建议与业务服务同机部署或单独部署 |
2. 部署 Config Server 副本集
① 配置文件关键段(3 台 Config Server 通用):
# /etc/mongod.conf
net:
port: 27019
bindIp: 0.0.0.0
replication:
replSetName: "cfg-rs"
sharding:
clusterRole: "configsvr" # 声明该节点是 Config Server
② 初始化 Config Server 副本集(在任一节点执行):
rs.initiate({
_id: "cfg-rs",
configsvr: true,
members: [
{ _id: 0, host: "cfg-01:27019" },
{ _id: 1, host: "cfg-02:27019" },
{ _id: 2, host: "cfg-03:27019" }
]
});
3. 将 Shard 节点声明为副本集
每个 Shard 本身就是一个副本集,部署方式与 2.2.1 完全一致。唯一区别是在配置文件中增加一行:
sharding:
clusterRole: "shardsvr" # 声明该节点是分片服务
假设你有两个 Shard 副本集:shard-rs-01 和 shard-rs-02,分别按照副本集步骤完成部署和初始化。
4. 启动 mongos 路由
mongos 不需要配置 dbPath(它不存储数据),只需指定 Config Server 的地址:
mongos --configdb "cfg-rs/cfg-01:27019,cfg-02:27019,cfg-03:27019" \
--bind_ip_all \
--logpath /var/log/mongos/mongos.log \
--fork
# 或者当 mongos 有对应 systemd unit 文件时:
# 在 /etc/mongos.conf 中配置上述参数,然后 systemctl start mongos
5. 在 mongos 上添加 Shard 并启用分片
所有 Shard 添加完毕之后,才能对具体集合开启分片:
// ① 连接到 mongos
mongosh mongos-host:27017
// ② 添加 Shard
sh.addShard("shard-rs-01/shard-01a:27017,shard-01b:27017,shard-01c:27017");
sh.addShard("shard-rs-02/shard-02a:27017,shard-02b:27017,shard-02c:27017");
// ③ 为数据库开启分片(必须)
sh.enableSharding("app_logs");
// ④ 对指定集合按 timestamp 做范围分片
sh.shardCollection("app_logs.logs", { "timestamp": 1 });
6. 分片命令详解——以 logs 集合按时间范围分片为例
sh.shardCollection("app_logs.logs", { "timestamp": 1 }) 执行后,MongoDB 会自动在 timestamp 字段上创建索引(如果尚未存在),并根据 timestamp 值的范围将文档分配到不同 Shard。
这条命令背后发生了什么?
// 验证分片状态
sh.status(); // 查看所有分片的 chunk 分布
db.logs.getShardDistribution(); // 查看 logs 集合在各 shard 上的 chunk 数量与大小
常见踩坑:
- 片键必须有索引:
sh.shardCollection要求片键字段上必须存在索引(若没有 MongoDB 会自动创建升序索引),所以如果使用的是复合片键如{ timestamp: 1, userId: 1 },需要确保该复合索引事先存在。 - 按时间范围分片可能产生热点:如果写入全是当前时间,所有新 chunk 都会落到同一个 Shard。写入压力极大的场景建议搭配 hashed sharding(使用
sh.shardCollection("db.logs", { "timestamp": "hashed" })),均匀分散写入负载,代价是无法高效执行范围查询。 - 关闭 Balancer 再做敏感操作:在手动
moveChunk之前建议先sh.stopBalancer(),操作完成后再sh.startBalancer()。
3. 配置篇:管理与维护的关键参数
MongoDB 的配置主要集中在 /etc/mongod.conf,YAML 格式,层级分明。
3.1 全局管理配置
| 配置块 | 参数 | 作用 | 建议值 |
|---|---|---|---|
net | port | 数据库监听端口 | 27017 |
net | bindIp | 绑定的网络接口 | 集群用 0.0.0.0,单机按需 |
security | authorization: "enabled" | 开启用户权限认证 | 生产环境必须开启 |
systemLog | path | 日志文件路径 | /var/log/mongodb/mongod.log |
systemLog | logRotate: "reopen" | 配合 logrotate 自动切分 | 开启 |
storage | dbPath | 数据存储目录 | /var/lib/mongo |
storage | engine: "wiredTiger" | 默认存储引擎 | 保持默认 |
net.http | enabled: false | 旧版 HTTP 管理页面 | 关闭,已不推荐使用 |
production 配置模板片段:
storage:
dbPath: /data/mongo
journal:
enabled: true
wiredTiger:
engineConfig:
cacheSizeGB: 4
net:
bindIp: 0.0.0.0
port: 27017
security:
authorization: enabled
operationProfiling:
mode: slowOp
slowOpThresholdMs: 100
3.2 日常维护配置详解
3.2.1 wiredTiger 存储引擎调优
cacheSizeGB:WiredTiger 内部缓存大小,默认取 (内存 - 1GB) 的 50%。如果机器只有 8GB 内存,建议手动设定为2-3,不要让 MongoDB 将内存占满,否则容易触发 OOM Killer。directoryForIndexes: true:将索引存储到独立子目录。高并发写入场景可减少 I/O 争抢。blockCompressor: "snappy":数据块压缩算法,snappy压缩比低但速度最快;zstd压缩比高但 CPU 开销稍大。
3.2.2 慢查询与监控
operationProfiling:
mode: slowOp # 只记录慢查询
slowOpThresholdMs: 100 # 超过 100 毫秒即记录
slowOpSampleRate: 0.5 # 仅采样 50% 慢查询,避免日志洪流
通过 db.system.profile.find().sort({ ts: -1 }).limit(10).pretty() 可以实时分析最慢的查询。
3.2.3 连接池与守护进程
processManagement:
fork: true # 以守护进程运行
pidFilePath: /run/mongod.pid
net:
maxIncomingConnections: 65536 # 最大连接数,默认较大,可按需调小
4. 运维实战:常见问题与解决方案
本章列出 MongoDB 在日常运行中最常遇到的问题,并按单机和集群两大场景分别给出详细可操作的排查与解决步骤。
4.1 单机模式疑难排障
4.1.1 排查工具速查表
| 问题现象 | 优先诊断命令 | 根因方向 |
|---|---|---|
| MongoDB 无法启动 | sudo journalctl -u mongod -n 50 --no-pager | 端口冲突 / 磁盘满 / 权限错误 / 配置语法错误 |
写入极慢,iostat 显示 util 接近 100% | db.serverStatus().wiredTiger.cache | 磁盘 I/O 瓶颈,或缓存配置过小导致大量 Page Fault |
| 暴力断电后启动失败 | grep -i "WT_CORRUPTION|Assertion" /var/log/mongodb/mongod.log | 存储引擎文件损坏 |
| 内存持续增长直到 OOM Kill | db.serverStatus().mem | 未对 WiredTiger 缓存做硬限制 |
| 单表数据量过大,频繁卡死 | db.collection.stats().indexSizes 与 explain("executionStats") | 缺少索引或全表扫描 |
4.1.2 详细操作步骤
1. 数据库无法启动
整个过程分为三步:排查 → 修复 → 验证。
① 查看最近的错误日志,定位具体报错:
sudo journalctl -u mongod -n 50 --no-pager
# 如果 journald 没有日志,直接查看文件:
sudo tail -n 100 /var/log/mongodb/mongod.log
② 根据报错类型修复:
- 如果日志显示
Failed to parse config file或Unrecognized option:# 验证配置文件语法 sudo mongod --config /etc/mongod.conf --validate # 根据输出修正对应行,常见错误是 YAML 缩进使用了 Tab - 如果日志显示
Address already in use:# 查谁占了 27017 sudo ss -tlnp | grep 27017 # 如果冲突进程不需要,直接 kill;否则修改 mongod.conf 的 port 为其他值 - 如果日志显示
Permission denied且路径指向数据目录:sudo chown -R mongod:mongod /var/lib/mongo # 默认路径 # 若改了 dbPath,替换为实际路径 - 如果日志显示
No space left on device:df -h /var/lib/mongo # 确认磁盘使用率,清理或扩容
③ 修复后重新启动并确认:
sudo systemctl start mongod
sudo systemctl status mongod
mongosh --eval "db.runCommand({ connectionStatus: 1 })"
2. 写入极慢 / 磁盘 I/O 跑满
① 立即诊断——看缓存命中率:
var s = db.serverStatus().wiredTiger.cache;
print("当前缓存大小(GB):" + s["bytes currently in the cache"] / 1024 / 1024 / 1024);
print("未命中导致读盘的次数:" + s["pages read into cache"]);
print("未命中后请求新页面的次数:" + s["pages requested from the cache"]);
如果 pages read into cache(从磁盘读到缓存)远大于正常范围,说明缓存严重不足,大量请求在穿透磁盘。
② 调整缓存大小:
编辑 /etc/mongod.conf,在 storage.wiredTiger.engineConfig 下设定硬上限:
storage:
wiredTiger:
engineConfig:
cacheSizeGB: 4 # 根据物理内存的 40%-50% 设置,但不超过 10GB(WiredTiger 内部限制)
调完后重启 MongoDB:sudo systemctl restart mongod
③ 补充验证:
// 运行 explain 查看当前慢查询是否使用了索引
db.getSiblingDB("your_db").your_collection.find({...}).explain("executionStats")
如果 stage 显示 COLLSCAN(全表扫描),说明还需要创建索引,这是导致磁盘 I/O 高的另一常见原因:
db.your_collection.createIndex({field: 1})
3. 暴力断电后启动失败(数据文件损坏)
① 确认损坏类型:
grep -i "WT_CORRUPTION\|Assertion" /var/log/mongodb/mongod.log | tail -20
出现 WT_CORRUPTION 关键字说明 WiredTiger 数据文件已损坏。
② 执行修复(Data Repair):
# ⚠️ 重要:修复前先备份当前损坏的数据目录!
sudo cp -a /var/lib/mongo /var/lib/mongo.backup.$(date +%Y%m%d_%H%M%S)
# 执行修复(会阻塞直到完成;期间数据库不可用)
sudo mongod --repair --dbpath /var/lib/mongo
经验值:100GB 数据修复大约需要 30-60 分钟(取决于磁盘速度),修复过程中会写入全新数据文件,建议预留原数据 1.5 倍的空闲磁盘空间。
③ 修复后重启并校验数据:
sudo systemctl start mongod
mongosh --eval "db.getSiblingDB('admin').runCommand({ listDatabases: 1 })"
如果修复成功但部分数据丢失,WiredTiger 会在日志中明确记录跳过的损坏页面。
4. 内存持续增长直到 OOM Kill
① 查看当前缓存占用:
db.serverStatus().wiredTiger.cache
关注 "bytes currently in the cache" 字段,如果已经接近物理内存的 80%,就要立刻限制。
② 紧急恢复——临时限流:
// 临时将缓存压到 2GB(重启后失效)
db.adminCommand({ setParameter: 1, wiredTigerEngineRuntimeConfig: "cache_size=2G" })
③ 持久化配置:编辑 /etc/mongod.conf,添加 cacheSizeGB(见上一问题步骤②),然后重启。
④ 监控验证:
# 重启后通过系统层面监控 30 分钟,确认内存不再蹭蹭上涨
free -h && top -bn1 | grep mongod
5. 单表数据量过大导致查询卡顿
① 紧急处理——立刻找到慢查询并杀死:
// 查看运行超过 5 秒的操作
db.currentOp({"active": true, "secs_running": {$gt: 5}})
// 记下 opid,立即 kill
db.killOp(<opid>)
② 短期方案——创建合适索引:
// 先用 explain 确认查询是否走了索引
db.your_collection.find({...}).explain("executionStats")
// 若 winningPlan.stage 是 COLLSCAN,则按查询条件创建索引
db.your_collection.createIndex({query_field: 1})
注意:建索引期间会阻塞该集合的写操作;生产大表建议在业务低峰期执行,或使用
background: true(4.2 及以上版本已默认后台创建)。
③ 中期方案——设置 TTL 自动清理老旧数据:
// 在包含时间字段的集合上创建 TTL 索引,MongoDB 每 60s 清理一次
db.your_collection.createIndex({"created_at": 1}, {expireAfterSeconds: 2592000})
// 以上示例表示文档在创建 30 天后自动删除
④ 长期方案——规划分片集群:当单表数据超过内存 10 倍以上,建议升级为分片集群以分摊压力(详见 4.2 节)。
6. 杀死阻塞进程的完整方法
// ① 列出所有活跃操作
db.currentOp({"active": true}).inprog.forEach(function(op) {
printjson({
opid: op.opid,
secs_running: op.secs_running,
ns: op.ns,
command: op.command
});
});
// ② 找到阻塞源(超过阈值的目标操作),记下 opid
// ③ 安全 kill
db.killOp(<opid>);
// 如果 killOp 无效(操作卡在内核层面),改用更强硬的方式:
db.adminCommand({killOp: 1, op: <opid>});
⚠️ 注意:永远不要 kill
opid为-1的系统内部操作或replSet相关的复制操作,否则会导致集群状态异常。
7. 数据库健康检查“标准三板斧”
// ① 连接数:正常不应超过 maxIncomingConnections 的 70%
db.serverStatus().connections
// 关注 current / available / active 三个值
// ② 操作计数器:观察 opcounters 中 insert/query/update/delete 的速率是否突增
db.serverStatus().opcounters
// ③ 存储与索引命中率
var s = db.stats();
print("数据大小(MB):" + s.dataSize / 1024 / 1024);
print("索引大小(MB):" + s.indexSize / 1024 / 1024);
print("索引命中率 (B-tree):" + (s.indexStats.btree.accesses > 0
? (s.indexStats.btree.hits / s.indexStats.btree.accesses * 100).toFixed(1) + "%"
: "暂无统计"));
// ④ 慢查询实时监控:精准找出耗时操作
var ops = db.currentOp({"active": true, "secs_running": {$gte: 1}}).inprog;
if (ops.length > 0) {
ops.forEach(function(op) {
printjson({
opid: op.opid,
secs_running: op.secs_running,
op: op.op, // 操作类型:query / insert / update / command 等
ns: op.ns, // 操作的命名空间(库名.集合名)
planSummary: op.planSummary || "无(可能是内部命令)",
client: op.client // 发起该操作的客户端 IP:端口
});
});
} else {
print("当前没有运行超过 1 秒的操作");
}
/*
* 关键字段含义:
* - secs_running: 操作已执行的秒数,如果持续增长且 >5,说明该操作可能阻塞了其他请求。
* - op: 操作的具体类型。常见值有 "query"(查询)、"insert"(插入)、"update"(更新)、
* "command"(管理命令,如 getMore / findAndModify 等)。如果看到大量 "query" 且时间很长,
* 通常是缺索引或查询条件不够精确。
* - ns: 目标命名空间,通过它快速定位是哪个库和集合出了问题,配合 explain() 进一步分析。
* - planSummary: 当前使用的查询计划摘要(仅在 op 为 "query" 时有)。如果显示 "COLLSCAN",
* 说明正在全表扫描,必须立刻创建索引。有效的查询计划会显示 "IXSCAN {field: 1}" 等。
*/
// ⑤ 操作延迟(latency)与队列(queue):洞察内部瓶颈
var m = db.serverStatus().metrics;
print("\n=== 操作延迟(单位:微秒)===");
print(" reads 总耗时:" + m.operation.reads + " μs,平均延迟:" +
(m.operation.reads / (db.serverStatus().opcounters.query || 1)).toFixed(2) + " μs");
print(" writes 总耗时:" + m.operation.writes + " μs,平均延迟:" +
(m.operation.writes / (db.serverStatus().opcounters.insert || 1)).toFixed(2) + " μs");
print("\n=== 文档级操作延迟(单位:微秒)===");
print(" deleted 总耗时:" + m.document.deleted + " μs");
print(" inserted 总耗时:" + m.document.inserted + " μs");
print(" updated 总耗时:" + m.document.updated + " μs");
print("\n=== 队列等待(仅在 WiredTiger 下有意义)===");
print(" readers 队列中的请求数:" + m.queryExecutor.scanned);
print(" writers 队列中的请求数:" + (m.ttl.deletedDocuments || 0));
/*
* 关键指标解读:
*
* ■ 操作延迟(metrics.operation)
* - reads / writes 是自 MongoD 启动以来的**累计耗时**(微秒)。要得到平均延迟,需要
* 用累计耗时除以对应操作的 opcounters(如 query 次数、insert 次数)。
* - 如果 reads 平均延迟持续 > 10,000 μs(即 10ms),说明读操作压力大,可能需要优化
* 查询、增加索引或扩展读副本。
* - 如果 writes 平均延迟持续 > 5,000 μs,说明写负载超过了当前节点的处理能力,应
* 检查是否磁盘 I/O 瓶颈、是否缺少写优化索引、或者是否在副本集上做了过多
* writeConcern: "majority" 的强写确认。
* - MongoDB 没有官方的“ops/s”指标直接暴露,但你可以通过连续两次执行
* db.serverStatus().opcounters 并计算差值来得到每秒操作数:
*
* var op1 = db.serverStatus().opcounters;
* sleep(1000);
* var op2 = db.serverStatus().opcounters;
* print("query ops/s: " + (op2.query - op1.query));
* print("insert ops/s: " + (op2.insert - op1.insert));
* print("update ops/s: " + (op2.update - op1.update));
*
* 配合平均延迟,就能算出每个操作的吞吐量(ops/s 越高越好,延迟越低越好)。
*
* ■ 文档级延迟(metrics.document)
* - 比 operation 更细粒度,精确到每个文档的 inserts / updates / deletes 总耗时。
* - 如果 document.updated 远大于 document.inserted,说明更新操作比新增更耗时,
* 通常是更新条件没有命中索引,或者更新操作触发大量 B-tree 页分裂。
*
* ■ 队列(queryExecutor.scanned)
* - 当读请求超过 MongoDB 内部线程池的处理能力时,请求会进入排队。
* - scanned 值持续增长 > 1000,说明读请求积压严重,需增加 Secondary 节点读分离,
* 或优化查询索引来缩短每个请求的处理时间。
*/
4.2 副本集与分片集群排障
4.2.1 集群排障速查表
| 问题现象 | 优先诊断命令 | 关键解决方向 |
|---|---|---|
从节点长时间卡在 STARTUP2 或 RECOVERING | rs.status() + rs.printSlaveReplicationInfo() | Oplog 窗口不足,触发全量同步 |
| 主节点宕机后整个集群无响应 | 在任意存活的 Secondary 上执行 rs.status() 查 "myState" | 可能因失去多数投票权导致无法选出新主 |
| Chunk 分配不均,热点 Shard 负载过高 | 在 mongos 上执行 sh.status() | Balancer 未开启或片键设计不合理 |
4.2.2 详细操作步骤
1. 从节点卡在 STARTUP2 / RECOVERING
① 确认延迟量与 Oplog 窗口:
rs.printSlaveReplicationInfo()
输出示例:
source: mongo-rs02:27017
syncedTo: Mon Jul 26 2026 08:35:12 GMT+0800 (CST)
4 secs (0 hrs) behind the primary
如果延迟秒数持续增长,说明从节点追不上主节点的写入速度。
② 检查当前 Oplog 大小:
rs.printReplicationInfo()
关注 oplog first event time 与 last event time 之间的时间跨度。这个跨度就是 Oplog 的记录窗口。如果从节点落后时间超过这个窗口,就会转为 RECOVERING 状态并进入全量同步。
③ 增大 Oplog(避免反复全量同步):
// 先将从节点从副本集中移除
rs.remove("mongo-rs03:27017")
// 重启该节点,指定更大的 oplogSizeMB
// 方式:在 /etc/mongod.conf 中设置
// replication:
// oplogSizeMB: 20480 # 20GB
// 然后重新加入副本集
rs.add("mongo-rs03:27017")
⚠️ 警告:不要在主节点上直接调大 Oplog,正确做法是逐台从节点轮替操作,始终保证多数节点在线。
2. 主节点宕机后集群无响应(选主失败)
① 在任意存活的 Secondary 上查看集群状态:
rs.status()
重点检查 members[].stateStr 和 members[].health。如果所有存活节点都处于 SECONDARY 且 "no primary" 持续超过 10 秒,说明选主失败。
② 分析失败原因:
最常见的原因是存活节点数未能超过半数。例如 3 节点集群挂了 2 台,仅剩 1 台 Secondary 无法自己投票选自己为主。此时 rs.status() 会显示 "votingMembersCount": 1 但 "writableVotingMembersCount": 2。
③ 紧急恢复方案:
// ⚠️ 仅限紧急情况,确认多数节点永久不可用后再执行!
// 在唯一存活的节点上强制将自己重新配置为主节点
var cfg = rs.conf();
cfg.members = [cfg.members.find(m => m.host.includes("存活的IP或host"))];
cfg.members[0].priority = 1;
cfg.members[0].votes = 1;
rs.reconfig(cfg, {force: true});
执行完后 MongoDB 会强制恢复单节点写服务。恢复后务必第一时间修复宕机节点并重新加入副本集,恢复原始配置。
3. Chunk 分配不均导致热点 Shard
① 在 mongos 上查看分片分布:
sh.status()
核对每个 Shard 的 Chunk 数量,如果某个 Shard 的 Chunk 数远多于其他,说明数据严重倾斜。
② 确认 Balancer 是否在正常运行:
sh.isBalancerRunning()
// 返回 true 说明 Balancer 正在迁移 Chunk;false 则需手动开启
sh.startBalancer()
③ 如果自动均衡无效,手动调整:
// 找到热点 Chunk 范围
use config
db.chunks.find({shard: "过载Shard名称"}).limit(5).pretty()
// 手动将指定 Chunk 迁移到其他 Shard
sh.moveChunk("your_db.your_collection", { shard_key_field: MinKey }, "目标Shard名称")
splitAt和moveChunk是性能密集型操作,务必在业务低峰期执行,并提前在测试环境演练。
5. 实战案例:一次完整的性能调优演练
前面的章节讲解了各种问题的排查方法,但实际工作中往往是多个问题叠加出现。本章用一个贴近真实的业务场景,带你走一遍「发现问题 → 定位根因 → 逐步调优 → 验证效果」的完整闭环,并指出每一步可能踩到的坑。
5.1 案例背景与初始症状
假设你负责一个电商订单系统,MongoDB 单机部署(8 核 16GB 内存),存储 orders 订单集合,日均写入约 200 万条,数据量已增长到约 80GB。最近一周业务方频繁反馈:
- 订单查询接口 P99 延迟从 80ms 飙升到 2.3s;
- 高峰期出现连接超时,部分请求直接报
MongoTimeoutException; - 服务器 CPU 使用率长期 90%+,
load average超过 12; - 偶发 OOM Killer 把 mongod 进程杀掉。
可能存在的问题(先别急着动手):这类症状组合出现时,新手最容易犯的错误是一上来就调大缓存或加索引。正确做法是先采集证据,用数据说话。
5.2 第一步:采集基线数据(诊断)
① 查看系统层指标:
# CPU / 内存 / 负载
top -bn1 | head -20
free -h
iostat -x 1 3 # 观察 %util 和 await
# 确认 mongod 是否被 OOM 杀过
dmesg -T | grep -i "oom\|killed process" | tail -10
② 查看 MongoDB 内部指标:
// 连接数是否打满
db.serverStatus().connections
// 操作计数器:判断读写比例
db.serverStatus().opcounters
// 慢查询:最近 20 条最慢的操作
db.system.profile.find().sort({ ts: -1 }).limit(20).pretty()
// 当前是否有长时间运行的操作
db.currentOp({"active": true, "secs_running": {$gt: 2}}).inprog.forEach(function(op) {
printjson({opid: op.opid, secs_running: op.secs_running, ns: op.ns, planSummary: op.planSummary});
});
③ 定位最慢的查询语句:
// 从 profile 中提取执行时间最长的查询
db.system.profile.find({op: "query"}).sort({millis: -1}).limit(5).forEach(function(doc) {
print("耗时(ms): " + doc.millis + " | 集合: " + doc.ns);
print("查询条件: " + JSON.stringify(doc.command));
print("---");
});
诊断结果:发现大量 COLLSCAN(全表扫描)的查询,集中在 orders 集合上按 userId 和 status 字段过滤;同时连接数已接近 maxIncomingConnections 上限。
5.3 第二步:定位根因(分析)
① 用 explain 确认查询计划:
db.orders.find({userId: "U12345", status: "PAID"}).explain("executionStats")
输出中 winningPlan.stage 显示 COLLSCAN,docsExamined 高达 300 万,而 nReturned 只有 12 条——扫描了 300 万条只返回 12 条,这就是性能瓶颈的直接证据。
② 检查现有索引:
db.orders.getIndexes()
发现只有 _id 默认索引和 createTime 单字段索引,完全没有覆盖 userId + status 的查询。
③ 检查连接数为何打满:
// 查看每个客户端 IP 的连接数
db.adminCommand({currentOp: 1, $all: true}).inprog.reduce(function(acc, op) {
var client = op.client ? op.client.split(":")[0] : "unknown";
acc[client] = (acc[client] || 0) + 1;
return acc;
}, {})
发现来自应用服务器的连接数异常高,怀疑是应用侧连接池配置过大,且存在连接泄漏(未正确关闭)。
可能存在的问题:只加索引不解决连接泄漏,高峰期依然会打满连接;只调连接池不建索引,CPU 依然被全表扫描拖垮。两个问题必须同时处理。
5.4 第三步:制定并执行调优方案(解决)
① 创建复合索引(解决全表扫描):
// 按查询条件创建复合索引:等值字段在前,排序字段在后
db.orders.createIndex({userId: 1, status: 1, createTime: -1})
注意:建索引会阻塞该集合的写操作。80GB 的大表建议在业务低峰期执行,并先用
db.orders.stats().indexSizes预估索引占用空间,确保磁盘有足够余量。
② 调整 WiredTiger 缓存(缓解内存压力):
# /etc/mongod.conf
storage:
wiredTiger:
engineConfig:
cacheSizeGB: 6 # 16GB 内存的 40% 左右,留出余量给 OS 和连接
可能存在的问题:缓存设得过大反而会触发 OOM。16GB 内存机器设 10GB 缓存,加上连接线程和文件系统缓存,很容易被内核 OOM Killer 盯上。
③ 优化应用侧连接池(解决连接打满):
// Java 驱动连接池配置:合理设置上限,避免无限制创建连接
MongoClientSettings settings = MongoClientSettings.builder()
.applyConnectionString(new ConnectionString("mongodb://localhost:27017"))
.applyToConnectionPoolSettings(builder -> builder
.maxSize(50) // 最大连接数,按业务并发合理设置
.minSize(5) // 最小空闲连接
.maxConnectionIdleTime(30, TimeUnit.SECONDS) // 空闲超时回收
.maxConnectionLifeTime(60, TimeUnit.MINUTES) // 连接最大存活时间
)
.build();
MongoClient mongoClient = MongoClients.create(settings);
可能存在的问题:连接池调太小会导致请求排队等待连接;调太大又会耗尽数据库连接。建议结合压测结果逐步调整,并在代码中确保每次操作后正确关闭游标和客户端,防止连接泄漏。
④ 开启慢查询日志(持续监控):
# /etc/mongod.conf
operationProfiling:
mode: slowOp
slowOpThresholdMs: 200 # 先放宽到 200ms,稳定后再收紧到 100ms
slowOpSampleRate: 1.0 # 全量记录,便于定位问题
5.5 第四步:验证效果与持续监控(复盘)
① 验证索引是否生效:
db.orders.find({userId: "U12345", status: "PAID"}).explain("executionStats")
确认 winningPlan.stage 已变为 IXSCAN,docsExamined 从 300 万降到 12,executionTimeMillis 从 1800ms 降到 3ms。
② 对比调优前后指标:
| 指标 | 调优前 | 调优后 | 说明 |
|---|---|---|---|
| P99 查询延迟 | 2.3s | 45ms | 复合索引生效 |
| CPU 使用率 | 90%+ | 35% | 不再全表扫描 |
| 连接数 | 打满 65536 | 稳定在 200-300 | 连接池合理化 |
| OOM 事件 | 每周 2-3 次 | 0 次 | 缓存合理配置 |
③ 持续监控脚本(定期巡检):
// 每周执行一次,输出关键健康指标
function healthCheck() {
var status = db.serverStatus();
var conn = status.connections;
var mem = status.mem;
print("=== MongoDB 健康检查 ===");
print("连接数: " + conn.current + " / " + conn.available);
print("内存占用: " + (mem.resident / 1024).toFixed(1) + " GB");
print("缓存命中率: " +
(status.wiredTiger.cache["pages requested from the cache"] > 0 ?
(100 - status.wiredTiger.cache["pages read into cache"] /
status.wiredTiger.cache["pages requested from the cache"] * 100).toFixed(1) + "%" :
"暂无数据"));
// 检查是否有长时间运行的操作
var longOps = db.currentOp({"active": true, "secs_running": {$gt: 10}}).inprog;
if (longOps.length > 0) {
print("⚠️ 发现 " + longOps.length + " 个运行超过 10 秒的操作!");
longOps.forEach(function(op) {
print(" opid=" + op.opid + " ns=" + op.ns + " secs=" + op.secs_running);
});
} else {
print("✅ 无长时间运行的操作");
}
}
healthCheck();
5.6 案例总结:调优方法论与常见误区
调优方法论(四步闭环):
- 采集基线:先量化问题(延迟、CPU、连接数、慢查询),不要凭感觉。
- 定位根因:用
explain和currentOp找到真正的瓶颈,区分「缺索引」「连接泄漏」「缓存不足」。 - 逐步调优:一次只改一个变量,改完立即验证,避免多个变更互相干扰。
- 持续监控:调优不是一次性工作,要建立巡检机制防止问题复发。
可能存在的问题与误区:
| 误区 | 后果 | 正确做法 |
|---|---|---|
一上来就调大 cacheSizeGB | 触发 OOM Killer,数据库直接宕机 | 先确认是否真的缓存不足,再按内存 40%-50% 设置 |
| 盲目创建大量索引 | 写性能下降、磁盘占用暴涨 | 只为高频查询创建索引,复合索引注意字段顺序 |
| 只加索引不解决连接泄漏 | 高峰期连接依然打满 | 同时排查应用侧连接池配置和代码中的连接管理 |
| 调优后不验证就收工 | 问题可能没解决甚至更糟 | 用 explain 和压测数据对比调优前后效果 |
| 忽略业务低峰期操作 | 建索引/迁移数据阻塞线上写入 | 大表操作务必安排在低峰期,并预留足够磁盘空间 |
核心心法:MongoDB 调优不是「调参大赛」,而是用数据定位瓶颈、用最小改动解决问题。遇到性能问题先问三个问题:查询走索引了吗?连接数合理吗?缓存配置匹配内存吗?——80% 的问题都出在这三处。
6. 原理解析:存储引擎与集群数据同步
6.1 存储引擎 WiredTiger
MongoDB 3.2 后默认使用 WiredTiger 存储引擎,而不是老的 MMAPv1。它的三个核心特性让你不需要懂内核也能记住:
- 文档级并发控制:MMAPv1 只能在库级别加锁,WiredTiger 实现了 MVCC(多版本并发控制),允许两个更新操作同时操作同一集合的不同文档而互不阻塞。
- 压缩:支持 Snappy / Zstd,实测可压缩 60% 以上的磁盘占用。
- Checkpoint + Journal 双重保险:WiredTiger 每 60 秒做一次全量数据快照(Checkpoint),同时在两次 Checkpoint 之间写 Journal 日志(WAL 机制的变体)。即使数据库意外崩溃,MongoDB 也能从上一个 Checkpoint 重放 Journal,恢复未写入的数据。
6.2 副本集数据同步机制
每一个写入 Primary 的操作会被记录为一个 Oplog(操作日志) 条目,idempotent(幂等)是它的关键设计:
- Secondary 节点会不断拉取 Primary 节点的 Oplog。
- 拉取到本地后,在自己的数据集中重放一遍这些操作。
- 当 Primary 宕机后,拥有最新 Oplog 的 Secondary 会被选举为新主。
这也能解释为什么从节点稍微有延迟是正常的——异步复制天然存在毫秒级滞后。
6.3 分片与路由
当你数据量超过单机承载极限时,就需要引入 mongos 路由和 Config Server。开发者感觉不到分片存在,mongos 会根据**片键(Shard Key)**将请求精准路由到目标 Shard。片键选择遵循“高读写分离度”原则,最忌讳挑选单调递增的字段(例如时间戳),会导致写入热点。
7. 总结与学习路线
本文从单机安装的“坑”开始(覆盖有网与离线双场景),带你平滑过渡到副本集、分片集群的完整部署,再到 mongod.conf 中每个关键参数的含义与最佳实践,最后结合 WiredTiger 与 Oplog 机制把底层原理兜底说清。Mermaid 图表贯穿全文,帮你用一张图记住体系结构。
下一步深入建议:
- 阅读 MongoDB 官方 Atlas 文档 了解云托管最佳实践。
- 深挖 WiredTiger 缓存调优 并尝试
cacheEviction参数调整。 - 搭建一个简单的 Docker Compose 副本集 环境,真实模拟故障转移。
只有亲手配过 rs.reconfig、手动 moveChunk 排过热点,你才算真正掌握了 MongoDB。

962

被折叠的 条评论
为什么被折叠?



