这是《MongoDB 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 性能调优目标是满足业务 SLA,而不是追求单条查询极限。MongoDB 调优通常从查询和索引开始,再处理 Schema、cache、磁盘、复制和分片。
25.1 调优流程
define latency targets
-> collect baseline
-> find slow operations
-> explain query
-> fix indexes / schema
-> tune workload
-> test under concurrency
-> monitor result
目标示例:
| 指标 | 目标 |
|---|---|
| 订单读 P99 | < 20 ms |
| 订单写 P99 | < 50 ms |
| 副本延迟 | < 1 s |
| 复制延迟峰值 | < 5 s |
| 慢查询 | < 0.1% |
25.2 查询优化
优先做:
- 高频查询加复合索引;
- 排序字段进索引;
- 投影减少返回;
- 游标分页;
$match前移;- 控制
$in大小; - 避免非前缀正则;
- 高频搜索使用专用索引。
验证:
db.orders.find({
user_id: "u_1001",
status: "PAID"
}).explain("executionStats")
目标:
IXSCAN
nReturned ≈ keysExamined
no SORT
25.3 写入优化
优化点:
- 减少无效索引;
- 批量写入控制批次;
- 拆小事务;
- 避免热点文档;
- 避免超大数组更新;
- 延迟 Secondary 负载;
- 控制文档增长;
- 大字段拆分;
- 使用合适的写关注。
批量写入:
db.orders.bulkWrite([
{ insertOne: { document: { _id: "o_1", status: "CREATED" } } },
{ insertOne: { document: { _id: "o_2", status: "CREATED" } } }
], { ordered: false })
25.4 Cache 调优
查看:
db.serverStatus().wiredTiger.cache
处理:
| 信号 | 优化 |
|---|---|
| pages read 高 | 增内存、缩工作集 |
| dirty pages 高 | 降写入、检查磁盘 |
| evict waiting | 降低并发或扩 cache |
| checkpoint 慢 | 减少脏页和索引 |
更有效的方向通常是减少工作集:拆冷数据、投影、减少索引和控制文档大小。
25.5 磁盘与文件系统
建议:
- 数据盘使用 SSD 或 NVMe;
- 监控 util、latency、IOPS;
- 预留空间;
- journal 与数据目录按官方建议部署;
- 避免与重 IO 服务混部;
- 定期验证快照性能;
- 关注磁盘队列和 iowait。
25.6 副本集性能
关注:
- Primary 写入延迟;
- Secondary 复制延迟;
- oplog 窗口;
- Secondary 读负载;
- 索引构建;
- 网络带宽;
- 心跳和选举健康。
优化:
- Secondary 只承担可延迟读;
- 报表使用 Hidden Secondary;
- 避免同时重建所有节点索引;
- 大事务拆小;
- 跨机房部署评估同步延迟。
25.7 分片性能
优化:
- 高频查询带分片键;
- 避免广播查询;
- 分片键分布均匀;
- 控制 jumbo chunk;
- balancer 低峰运行;
- mongos 足够实例;
- 减少跨 shard 事务;
- 监控每 shard 负载。
25.8 应用侧优化
- 使用连接池;
- 控制连接数;
- 设置查询超时;
- 使用读写关注策略;
- 请求携带 trace ID;
- 幂等重试;
- 避免每请求全量查询;
- 本地缓存热点数据;
- 批处理错峰;
- 熔断非核心查询。
25.9 压测
压测场景:
- 读写混合;
- 热点用户;
- 聚合报表;
- 大批量导入;
- Secondary 读;
- 分片迁移;
- Primary 切换;
- 索引上线;
- 长稳测试。
记录:
throughput
p95 / p99 latency
error rate
cache metrics
disk metrics
replication lag
application result
25.10 反模式
| 反模式 | 后果 |
|---|---|
| 只看平均延迟 | 掩盖长尾 |
| 索引越多越好 | 写入变慢 |
| 事务包住外部调用 | 长事务 |
| Secondary 读解决一切 | 读旧数据 |
| 分片解决慢查询 | 广播更慢 |
| 盲目调 cache | 抢占系统内存 |
| 生产直接建大索引 | 影响线上 |
本章小结
MongoDB 性能优化通常先修查询、索引和 Schema,再治理工作集、磁盘和复制延迟。写入端关注热点和索引成本,分片端关注路由和均衡。所有优化都必须通过并发压测和上线后监控验证。
思考题
- 为什么先优化查询再扩容?
- 工作集大于内存有什么表现?
- 索引过多的写入代价是什么?
- 分片为什么可能让慢查询更慢?
- 压测为什么必须包含长稳阶段?