接手了一个MongoDB项目,数据量涨到了900多万条,索引优化成了必修课。这里记录一些实际操作中踩过的坑。
索引创建方式
前台索引
默认情况下MongoDB用前台方式创建索引,这会把整个集合锁死:
1 | // 前台创建索引 - 阻塞所有读写 |
特点:
- 创建期间其他操作全部卡住
- 速度相对快一些
- 只适合维护窗口期或小数据量
后台索引
生产环境数据量大的时候,后台索引是救命稻草:
1 | // 后台创建索引 - 不阻塞读写 |
| 对比项 | 前台索引 | 后台索引 |
|---|---|---|
| 是否阻塞 | 是 | 否 |
| 创建速度 | 快 | 慢约40% |
| 资源占用 | 高 | 较低 |
| 使用场景 | 维护期 | 生产环境 |
注意:后台索引虽然不打断业务,但创建时间会更长,要做好时间规划。
千万级数据的实战经验
创建时间预估
亲身经历:927万条数据建索引,花了好几个小时。如果预估不足,会影响后续计划:
1 | // 查看集合统计信息 |
查看索引进度
使用db.currentOp()可以监控索引创建的实时进度:
1 | db.currentOp({ |
输出示例:
1 | { |
关键字段:
| 字段 | 说明 |
|---|---|
secs_running |
已运行秒数 |
msg |
进度百分比 |
progress.done |
已处理文档数 |
progress.total |
总文档数 |
终止索引创建
建索引过程中如果发现影响太大,可以干掉它:
1 | // 终止指定opid的操作 |
提示:
- 没建完的索引会被清理掉
- 已完成的不会受影响
- 重启后未完成的可能会变成前台模式继续
索引创建期间的限制
连接问题
建索引时:
- 执行创建的那个连接会被占用
- 要干别的需要开新连接
- 索引没建完之前不生效
- 建完立刻能用
这期间不能做的操作
| 操作 | 后果 |
|---|---|
repairDatabase |
失败 |
db.collection.drop() |
失败 |
compact |
失败 |
异常处理
mongod挂了怎么办:
- 重启后没建完的索引会变成前台模式继续
- 如果失败(比如重复键错误),mongod会报错退出
跳过失败的索引:
1 | # 启动时跳过索引创建 |
性能优化建议
资源规划
内存方面:
- 建索引需要足够内存
- 内存不够的话速度会显著下降
- 最好在业务低峰期操作
磁盘I/O:
- 后台索引对性能影响小一些
- 但还是会拖慢相关集合的操作
- 避开高峰期是明智之选
索引创建策略
分批创建:
1 | // 策略1:按业务优先级分批创建 |
覆盖索引:
1 | // 避免冗余索引 |
批量更新技巧
给数据加随机后缀
1 | // 将name为"Guest"的记录改为"Guest"+4位随机数 |
批量更新优化版:
1 | // 使用bulkWrite批量更新 |
索引设计与监控
设计原则
1 | // 1. 等值查询字段放前面 |
监控命令
1 | // 查看查询使用的索引 |
explain分析
1 | { |
七、最佳实践总结
7.1 索引创建Checklist
- 评估索引必要性(EXPLAIN验证)
- 选择后台创建方式(生产环境)
- 预估创建时间并选择维护窗口
- 监控创建进度(db.currentOp)
- 验证索引效果(EXPLAIN确认)
- 清理冗余索引
经验总结
| 场景 | 建议 |
|---|---|
| 数据量大 | 后台索引 |
| 并发高 | 分批创建,避开高峰 |
| 内存小 | 加内存或减少索引字段 |
| 写入多 | 控制索引数量 |
| 查询复杂 | 用复合索引 |
常用脚本
1 | // 查看所有集合索引大小 |
写在最后
MongoDB索引管理是个需要经验的活儿。后台索引、进度监控、批量更新这些技巧,在实际运维中帮了我不少忙。
几点心得:
- 生产环境一定要后台索引,别堵了业务
- 盯着进度,心里有底
- 复合索引设计好,减少冗余
- 批量更新用Bulk,效率高
- 定期看看慢查询,及时调整
参考: