MongoDB 数据库连接超时与性能故障排查实战

一、故障背景

MongoDB 作为广泛使用的 NoSQL 数据库,在生产环境中常遇到连接超时、查询性能下降等问题。本文基于实际运维案例,系统梳理 MongoDB 常见故障的排查思路与解决方案。

二、连接超时故障排查

2.1 故障现象

  • 应用端报错:MongoServerError: connection timed out
  • 连接池耗尽,新请求无法获取连接
  • 部分请求成功,部分请求超时

2.2 排查步骤

步骤 1:检查 MongoDB 服务状态

1
2
3
4
5
6
7
8
# 检查服务运行状态
systemctl status mongod

# 查看监听端口
netstat -tlnp | grep 27017

# 检查进程资源占用
top -p $(pgrep -d',' mongod)

步骤 2:检查连接数

1
2
3
4
5
6
7
8
# 登录 MongoDB
mongosh --host localhost --port 27017

# 查看当前连接数
db.serverStatus().connections

# 查看连接详情
db.currentOp({"secs_running": {"$gte": 10}})

关键指标解读:

  • current: 当前活跃连接数
  • available: 可用连接数
  • totalCreated: 累计创建连接数

连接数耗尽处理:

1
2
3
4
// 临时增加最大连接数(重启生效)
// 修改 /etc/mongod.conf
net:
maxIncomingConnections: 65536

步骤 3:检查网络连通性

1
2
3
4
5
6
7
8
9
10
11
# 从应用服务器测试连通性
telnet mongo-server 27017
nc -zv mongo-server 27017

# 检查防火墙规则
iptables -L -n | grep 27017
firewall-cmd --list-ports

# 检查网络延迟
ping -c 10 mongo-server
mtr mongo-server

步骤 4:分析慢连接

1
2
3
4
5
6
7
8
9
10
11
12
// 启用慢查询日志
db.setProfilingLevel(1, 100) // 记录>100ms 的操作

// 查看慢查询
db.system.profile.find().sort({ts: -1}).limit(10)

// 分析连接等待时间
db.currentOp().inprog.forEach(op => {
if (op.secs_running > 5) {
printjson(op)
}
})

2.3 常见原因与解决方案

原因 排查方法 解决方案
连接池配置过小 检查应用端连接池配置 增加 maxPoolSize
网络延迟高 mtr/ping 测试 优化网络或就近部署
连接泄漏 分析 currentOp 修复应用代码,设置 maxIdleTimeMS
服务端连接数上限 查看 serverStatus 调整 maxIncomingConnections
防火墙拦截 检查 iptables/firewalld 添加放行规则

三、查询性能故障排查

3.1 故障现象

  • 查询响应时间突然变慢
  • CPU 使用率飙升
  • 磁盘 IO 等待增高

3.2 排查步骤

步骤 1:启用性能分析

1
2
3
4
5
6
7
8
9
10
11
// 开启慢查询分析(阈值 100ms)
db.setProfilingLevel(1, 100)

// 查看慢查询统计
db.system.profile.count()

// 分析最慢的查询
db.system.profile.find(
{ millis: { $gt: 100 } },
{ op: 1, ns: 1, query: 1, millis: 1, ts: 1 }
).sort({ millis: -1 }).limit(10)

步骤 2:检查索引使用情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 分析查询执行计划
db.collection.find({ status: "active" }).explain("executionStats")

// 检查索引统计
db.collection.getIndexes()

// 查看索引使用情况(需重启后生效)
db.serverStatus().metrics.commands.explain

// 查找未使用索引的查询
db.system.profile.find({
"op": "query",
"execStats.stage": "COLLSCAN"
}).limit(10)

COLLSCAN 表示全表扫描,需要优化索引。

步骤 3:检查锁等待

1
2
3
4
5
// MongoDB 4.0+ 查看锁统计
db.serverStatus().locks

// 查看当前锁等待
db.currentOp().inprog.filter(op => op.locks && op.locks.Global.acquireWaitCount > 0)

步骤 4:检查内存与缓存

1
2
3
4
5
6
7
8
9
10
// 查看内存使用
db.serverStatus().mem

// 查看缓存使用
db.serverStatus().wiredTiger.cache

// 关键指标
// - resident: 物理内存使用
// - virtual: 虚拟内存使用
// - mapped: 映射文件大小

3.3 性能优化方案

方案 1:索引优化

1
2
3
4
5
6
7
8
9
10
11
// 创建复合索引
db.orders.createIndex({ customerId: 1, createdAt: -1 })

// 创建覆盖索引(Covered Query)
db.users.createIndex({ email: 1 }, { name: 1, status: 1 })

// 删除未使用索引
db.collection.dropIndex("unused_index")

// 重建索引(碎片整理)
db.collection.reIndex()

方案 2:查询优化

1
2
3
4
5
6
7
8
9
10
11
12
// 避免全表扫描
// ❌ 差:
db.users.find({ name: /john/i }) // 正则无法使用索引

// ✅ 好:
db.users.find({ name: "John" })

// 使用投影减少数据传输
db.users.find({ status: "active" }, { _id: 0, name: 1, email: 1 })

// 限制返回数量
db.orders.find().sort({ createdAt: -1 }).limit(100)

方案 3:分片策略

1
2
3
4
5
6
7
8
// 启用分片
sh.enableSharding("production")

// 选择分片键
sh.shardCollection("production.orders", { customerId: "hashed" })

// 查看分片状态
sh.status()

四、内存与磁盘故障排查

4.1 OOM 问题排查

1
2
3
4
5
6
# 检查系统 OOM 日志
dmesg | grep -i "killed process"
journalctl -k | grep -i "out of memory"

# 查看 MongoDB 内存使用
ps aux | grep mongod | awk '{print $6}'

解决方案:

1
2
3
4
5
# /etc/mongod.conf
storage:
wiredTiger:
engineConfig:
cacheSizeGB: 4 # 设置为系统内存的 50-60%

4.2 磁盘空间不足

1
2
3
4
5
6
7
8
# 检查磁盘使用
df -h

# 检查 MongoDB 数据目录
du -sh /var/lib/mongodb/*

# 查看集合大小
db.collection.stats()

清理方案:

1
2
3
4
5
6
7
8
// 删除过期数据(TTL 索引)
db.logs.createIndex({ createdAt: 1 }, { expireAfterSeconds: 2592000 })

// 压缩集合
db.runCommand({ compact: "collection_name" })

// 删除无用索引
db.collection.dropIndex("index_name")

4.3 磁盘 IO 性能问题

1
2
3
4
5
6
7
8
# 检查 IO 等待
iostat -x 1 5

# 检查 MongoDB IO 统计
mongostat --host localhost 1

# 查看慢 IO 操作
db.serverStatus().metrics.storage

五、复制集故障排查

5.1 主从同步延迟

1
2
3
4
5
6
7
8
// 查看复制集状态
rs.status()

// 查看同步延迟
rs.printSlaveReplicationInfo()

// 查看 oplog 大小
db.getReplicationInfo()

解决方案:

1
2
3
4
// 增加 oplog 大小(需重启)
// 修改 /etc/mongod.conf
replication:
oplogSizeMB: 10240

5.2 选举失败

1
2
3
4
5
6
7
8
// 检查成员优先级
rs.conf().members.forEach(m => print(m.host, m.priority))

// 强制重新选举
rs.stepDown()

// 查看选举日志
tail -f /var/log/mongodb/mongod.log | grep -i election

六、监控与告警配置

6.1 Prometheus + MongoDB Exporter

1
2
3
4
5
# prometheus.yml
scrape_configs:
- job_name: 'mongodb'
static_configs:
- targets: ['mongo-server:9216']
1
2
3
4
5
# 安装 MongoDB Exporter
docker run -d -p 9216:9216 \
--name mongodb-exporter \
percona/mongodb_exporter:0.40 \
--mongodb.uri="mongodb://user:pass@localhost:27017"

6.2 关键监控指标

指标 告警阈值 说明
mongodb_connections_usage >80% 连接池使用率
mongodb_memory_usage >90% 内存使用率
mongodb_disk_usage >85% 磁盘使用率
mongodb_replication_lag >60s 复制延迟
mongodb_query_duration_avg >500ms 平均查询耗时
mongodb_asserts_regular 持续增长 断言错误

七、故障排查流程图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
连接超时/性能下降

检查服务状态 (systemctl/netstat)

检查连接数 (serverStatus.connections)

检查网络连通性 (telnet/ping/mtr)

分析慢查询 (profiling)

检查索引使用 (explain)

检查锁等待 (currentOp)

检查内存/磁盘 (serverStatus.mem/cache)

定位问题并优化

八、总结

MongoDB 故障排查的核心思路:

  1. 先服务后网络:确认 MongoDB 服务正常,再排查网络问题
  2. 先指标后日志:通过 serverStatus 快速定位,再深入日志分析
  3. 先索引后查询:大部分性能问题源于索引缺失或使用不当
  4. 先监控后告警:建立完善的监控体系,提前发现问题

建立标准化的排查流程和监控体系,是保障 MongoDB 稳定运行的关键。


参考文档: