MongoDB 从安装到精通:单机·集群·原理·排障全指南

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 最佳实践与性能提示

  1. 连接池配置:使用单例模式创建 MongoClient,避免频繁创建连接。
  2. 批量操作:大量插入/更新时使用 insertManybulkWrite
  3. 索引优化:为查询条件字段创建索引,复合索引注意字段顺序。
  4. 投影优化:只查询需要的字段,减少网络传输。
  5. 游标管理:处理大量数据时使用游标分批获取,避免内存溢出。
// 批量写入示例
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

小技巧:如果依赖项较多,建议直接使用 repotrackreposync 下载全量依赖包到同一目录,避免传过去之后发现缺依赖。

补充:如果跳板机是 Windows,如何下载 RPM 包?
不少运维同学的“有网机器”其实是自己的 Windows 办公电脑。此时不需要配置 YUM 仓库,直接从 MongoDB 官方下载页面获取即可:

  1. 打开 MongoDB Community Server Download
  2. 版本选 6.0.x,平台选 Red Hat / CentOS(或与目标服务器一致的系统),包格式保持 tgz 或直接下载 RPM 包。
  3. 点击「Download」获取安装包;如果是 tgz 压缩包,解压后可找到所有 RPM 文件。
  4. 把下载好的 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 })"

离线安装核心注意点

  1. 操作系统版本必须匹配:在有网机器上必须使用与目标服务器完全相同的 CentOS 版本下载包,否则 glibc 等核心依赖版本不一致会导致安装失败。
  2. 架构一致:x86_64 和 ARM(aarch64)的包不能混用;下载前用 uname -m 确认。
  3. 离线依赖兜底:如果 yum localinstall 仍报依赖缺失(比如缺少 compat-openssl10),需要额外从 CentOS 安装 ISO 镜像中复制对应基础包一道传入。
2.1.3 常见安装报错与解决
报错内容原因解决方案
Requires: libcrypto.so.10CentOS 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 分发到各节点后统一安装。

应用驱动

mongos 路由层(至少 2 个)

Config Server 副本集
(3 节点,存储元数据)

Shard 01 副本集
(3 节点)

Shard 02 副本集
(3 节点)

Primary

Secondary

Arbiter

Primary

Secondary

Arbiter

Primary

Secondary

Arbiter

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 集群部署关键注意点
  1. host 配置格式:副本集初始化中 host 字段必须写可跨节点解析的 hostname 或 IP。不要写 localhost,否则从节点永远连不上主节点。
  2. bindIp:从 MongoDB 3.6 起,bindIp 默认值为 localhost,集群模式下必须改为 0.0.0.0 或者本机内网 IP。
  3. 防火墙:确保所有节点间 27017 端口互通。
  4. 时钟同步:所有节点必须启用 NTP 服务(chronydntpd),否则心跳检测会出问题,引发频繁切换主节点。
  5. 离线部署集群:提前在可联网机器上把各节点所需的 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 Server3(副本集)27019存储 chunk 元数据和 auth 信息
Shard 013(副本集)27017第一片数据存储,正式上线后可扩容
Shard 023(副本集)27017第二片数据存储
mongos227017无状态路由,建议与业务服务同机部署或单独部署

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-01shard-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。

这条命令背后发生了什么?

Shard 02 副本集

Primary

Secondary

Shard 01 副本集

Primary

Secondary

mongos 路由

logs 集合数据

// 验证分片状态
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 全局管理配置

配置块参数作用建议值
netport数据库监听端口27017
netbindIp绑定的网络接口集群用 0.0.0.0,单机按需
securityauthorization: "enabled"开启用户权限认证生产环境必须开启
systemLogpath日志文件路径/var/log/mongodb/mongod.log
systemLoglogRotate: "reopen"配合 logrotate 自动切分开启
storagedbPath数据存储目录/var/lib/mongo
storageengine: "wiredTiger"默认存储引擎保持默认
net.httpenabled: 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 Killdb.serverStatus().mem未对 WiredTiger 缓存做硬限制
单表数据量过大,频繁卡死db.collection.stats().indexSizesexplain("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 fileUnrecognized 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 集群排障速查表
问题现象优先诊断命令关键解决方向
从节点长时间卡在 STARTUP2RECOVERINGrs.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 timelast 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[].stateStrmembers[].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名称")

splitAtmoveChunk 是性能密集型操作,务必在业务低峰期执行,并提前在测试环境演练。

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 集合上按 userIdstatus 字段过滤;同时连接数已接近 maxIncomingConnections 上限。

5.3 第二步:定位根因(分析)

① 用 explain 确认查询计划

db.orders.find({userId: "U12345", status: "PAID"}).explain("executionStats")

输出中 winningPlan.stage 显示 COLLSCANdocsExamined 高达 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 已变为 IXSCANdocsExamined 从 300 万降到 12,executionTimeMillis 从 1800ms 降到 3ms。

② 对比调优前后指标

指标调优前调优后说明
P99 查询延迟2.3s45ms复合索引生效
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 案例总结:调优方法论与常见误区

调优方法论(四步闭环)

  1. 采集基线:先量化问题(延迟、CPU、连接数、慢查询),不要凭感觉。
  2. 定位根因:用 explaincurrentOp 找到真正的瓶颈,区分「缺索引」「连接泄漏」「缓存不足」。
  3. 逐步调优:一次只改一个变量,改完立即验证,避免多个变更互相干扰。
  4. 持续监控:调优不是一次性工作,要建立巡检机制防止问题复发。

可能存在的问题与误区

误区后果正确做法
一上来就调大 cacheSizeGB触发 OOM Killer,数据库直接宕机先确认是否真的缓存不足,再按内存 40%-50% 设置
盲目创建大量索引写性能下降、磁盘占用暴涨只为高频查询创建索引,复合索引注意字段顺序
只加索引不解决连接泄漏高峰期连接依然打满同时排查应用侧连接池配置和代码中的连接管理
调优后不验证就收工问题可能没解决甚至更糟explain 和压测数据对比调优前后效果
忽略业务低峰期操作建索引/迁移数据阻塞线上写入大表操作务必安排在低峰期,并预留足够磁盘空间

核心心法:MongoDB 调优不是「调参大赛」,而是用数据定位瓶颈、用最小改动解决问题。遇到性能问题先问三个问题:查询走索引了吗?连接数合理吗?缓存配置匹配内存吗?——80% 的问题都出在这三处。

6. 原理解析:存储引擎与集群数据同步

Shard-B Secondary Shard-A Primary Config Server mongos(路由) 客户端 Shard-B Secondary Shard-A Primary Config Server mongos(路由) 客户端 写入文档 {user:"John", age:30} 根据片键查询文档应落哪个 Shard 返回 Shard-A 插入数据 Primary 写入成功 异步复制 Oplog 返回写入成功

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(幂等)是它的关键设计:

  1. Secondary 节点会不断拉取 Primary 节点的 Oplog。
  2. 拉取到本地后,在自己的数据集中重放一遍这些操作。
  3. 当 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。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值