MongoDB 连接数异常增长及优化实践:从排查到提升系统稳定性
1. 背景:系统运行不稳定,接口性能波动
最近,我们的系统出现了一个棘手的问题:接口响应时间会在一段时间后显著变慢,而重启服务后又恢复正常。经过多次复现,我们发现:
- 接口在初始阶段表现良好,查询响应时间较短;
- 随着系统运行,MongoDB 连接数不断增长,最终稳定在一个高位,不再下降;
- 当连接数达到一定阈值后,查询变慢,影响系统稳定性。
为了解决这一问题,我们开始深入分析 MongoDB 连接管理、索引优化等方面的潜在问题,并制定了一系列优化措施。
2. 排查过程:找出 MongoDB 连接数异常增长的根因
2.1 观察 MongoDB 连接数变化
首先,我们通过 db.serverStatus().connections
监控 MongoDB 连接数:
1 | db.serverStatus().connections |
结果发现:
- current(当前连接数)不断增长;
- available(可用连接数)逐步减少;
- totalCreated(总创建连接数)远超预期。
此外,MongoDB 日志 (mongod.log
) 显示:
1 | I NETWORK [listener] connection accepted from 172.17.0.2:46732 #6159 (76 connections now open) |
说明我们的应用正在频繁创建新连接,但未能有效释放,导致连接数飙升。
2.2 Java 应用层排查:连接管理问题
在 Spring Boot 代码中,我们发现:
1 | public MongoClient getMongoClient() { |
- 问题 1:每次数据库请求都
new MongoClient()
,未复用连接池,导致连接数激增。[注:这是常见的低级错误;我们的工程中是多用户体系,每个用户拥有一个数据库,所以在查询用户时必须用到admin账户,而在单个用户业务时又需要新建单个用户的连接,所以采用了多用户复用的策略。在这点上,也可能存在资源浪费] - 问题 2:MongoDB 连接池未正确配置,连接资源未合理管理。
2.3 查询优化排查:索引问题
在数据库索引上,我们观察到:
- 查询(find 条件)和排序(sort 字段)使用的索引未做区分,导致索引扫描范围过大。
- $match 和 $sort 使用不同字段时未创建复合索引,查询效率受影响。
示例查询:
1 | db.order.aggregate([ |
如果 userId
和 createdAt
没有复合索引,MongoDB 可能会全表扫描,导致查询慢。
3. 解决方案:优化数据库连接和索引策略
针对以上问题,我们采取了以下优化措施:
3.1 连接管理优化:全局单例模式
我们修改 Java 代码,使用单例模式创建 MongoClient
,并配置合理的连接池:
1 | public class MongoConnectionManager { |
🔹 优化点:
- 确保整个应用只创建一个
MongoClient
实例,避免连接数无限增长。 - 连接复用,减少 MongoDB 资源消耗。[本项目用户复用]
3.2 配置 MongoDB 连接池
1 | MongoClientOptions options = MongoClientOptions.builder() |
🔹 优化点:
- 避免 MongoDB 连接过载;
- 降低连接频繁创建的开销。
3.3 设置 MongoDB 连接数上限
在 mongod.conf
中增加:
1 | net: |
🔹 优化点:
- 限制最大连接数,防止异常情况下连接耗尽。
3.4 索引优化策略
针对 sort
和 find
,我们采取不同索引策略:
(1) 单字段索引:适用于 $match
和 $sort
在同一字段
1 | db.logs.createIndex({ createdAt: -1 }) |
适用于查询:
1 | db.logs.find({ createdAt: { $gte: ISODate("2024-01-01") } }).sort({ createdAt: -1 }) |
✅ **索引 {createdAt: -1}
同时支持 $match
和 $sort
**。
(2) 复合索引:适用于 $match
和 $sort
在不同字段
1 | db.orders.createIndex({ userId: 1, createdAt: -1 }) |
适用于查询:
1 | db.orders.find({ userId: "abc123" }).sort({ createdAt: -1 }) |
✅ 避免 MongoDB 额外排序操作,提升查询效率。
4. 优化结果:系统性能提升
4.1 连接数控制
- 之前:连接数无限增长,高峰时 1000+。
- 现在:稳定在 30-50 之间,系统稳定性大幅提升。
4.2 查询性能提升
- 之前:通过监控慢查询,某些查询 **耗时 2s+**,高峰期更长。
- 现在:查询平均耗时 <100ms。
4.3 资源占用优化
- 之前:MongoDB CPU 负载飙升,磁盘 IO 高。[居然在这个内网服务器,没有GPU的,发现了挖矿程序,也是罕见]
- 现在:CPU 负载降低 40%,磁盘 IO 下降 50%。
5. 总结:MongoDB 连接和索引优化的关键
通过这次 MongoDB 优化,我们总结了以下经验:
- 连接管理:
- 统一
MongoClient
连接管理,避免频繁创建新连接。 - 合理配置连接池,控制最大连接数,释放空闲连接。
- 统一
- 查询优化:
- 针对
$match
频繁的字段创建单字段索引。 - 处理
$match
和$sort
不同字段时,创建复合索引。
- 针对
- MongoDB 配置优化:
- 限制
maxIncomingConnections
,防止意外连接暴涨。 - **监控
db.serverStatus().connections
**,及时发现异常增长。
- 限制
这次优化让我们对 MongoDB 的性能调优有了更深入的理解,同时也提升了系统的健壮性。如果你也遇到了类似问题,希望这篇文章能给你一些启发!🚀
6. 未来更进一步优化路线
优化方向 | 优化点 | 目标 |
---|---|---|
数据库架构 | 升级至 MongoDB 4.4+/5.x,考虑 Sharding | 提高扩展性,支持更大数据量 |
连接管理 | 连接池动态调整、读写分离、限制最大连接数 | 避免连接泄露,减少数据库负载 |
索引优化 | 自动检测无效索引,动态优化 find & sort |
提高查询性能,降低磁盘 IO |
缓存优化 | 使用 Redis 缓存高频查询 | 降低 MongoDB 访问压力 |
监控告警 | Prometheus + Grafana 实时监控 | 预警异常连接数增长,防止服务崩溃 |
未来扩展 | 迁移至 MongoDB Atlas / Kubernetes | 提供更稳定、可扩展的架构支持 |
- 本文作者: Linking
- 本文链接: https://linking.fun/2023/12/19/MongoDB-连接数异常增长及优化实践:从排查到提升系统稳定性/
- 版权声明: 版权所有,转载请注明出处!