处理千万级甚至亿级数据的 MongoDB 时,索引创建是个大问题。用错方式可能阻塞整个库,影响线上服务。这篇记录一下大数据量场景下的索引创建策略,包括前台/后台创建、进度监控、踩过的坑。
索引创建方式对比
前台创建(Foreground)
1 | // 默认方式:前台创建索引 |
特点:
- 阻塞集合上的所有读写操作
- 索引创建期间数据库无法提供正常服务
- 适用于维护窗口或新集合
1 | 时间线: |
后台创建(Background)
1 | // 后台创建索引 |
特点:
- 不阻塞集合的读写操作
- 数据库可以正常提供服务
- 创建时间较长
1 | 时间线: |
方式对比
| 特性 | 前台创建 | 后台创建 |
|---|---|---|
| 阻塞读写 | 是 | 否 |
| 创建速度 | 快 | 慢(约2-3倍) |
| 线上可用 | 否 | 是 |
| 资源占用 | 高 | 较低 |
| 回滚能力 | 无 | 有限 |
大数据量索引创建实战
千万级数据索引创建
1 | // 示例:927万条数据创建索引 |
创建复合索引:
1 | // 复合索引,包含稀疏选项 |
索引创建进度监控
使用 currentOp 查看进度
1 | db.currentOp({ |
返回结果解析:
1 | { |
关键字段说明:
| 字段 | 含义 | 示例值 |
|---|---|---|
secs_running |
已运行秒数 | 21 |
msg |
进度信息 | “62%” |
progress.done |
已完成文档数 | 3103722 |
progress.total |
总文档数 | 5000000 |
locks |
持有的锁 | Global: w |
进度监控脚本
1 | // 实时监控索引创建进度 |
终止索引创建
如果需要终止正在创建的索引:
1 | // 1. 先找到操作的 opid |
注意事项:
- 终止后需要重新创建索引
- 终止操作不会保留部分创建的索引
- 终止可能需要一些时间才能生效
索引创建期间注意事项
1. 会话阻塞问题
1 | 重要提示: |
Mongo Shell 示例:
1 | # 终端1:创建索引(此终端会被阻塞) |
2. 管理操作限制
索引创建期间,以下管理操作无法执行:
1 | // ❌ 以下操作在索引创建期间会被阻塞 |
3. 异常中断处理
MongoDB 重启后的行为
1 | 场景:后台创建索引期间 mongod 异常终止 |
性能优化策略
1. 选择合适的维护窗口
1 | // 即使使用后台创建,也建议在低峰期执行 |
2. 内存配置优化
1 | // 确保有足够的内存用于索引创建 |
3. 分批创建索引
对于超大规模数据,考虑先导入数据再创建索引:
1 | # 1. 导入数据(不带索引) |
4. 使用隐藏索引(MongoDB 4.4+)
1 | // 创建隐藏索引,测试性能影响 |
索引创建性能对比
实际测试数据
| 数据量 | 索引字段 | 前台创建 | 后台创建 | 性能影响 |
|---|---|---|---|---|
| 100万 | 单字段 | 30秒 | 90秒 | 无 |
| 1000万 | 单字段 | 5分钟 | 15分钟 | 轻微 |
| 5000万 | 单字段 | 30分钟 | 90分钟 | 明显 |
| 1000万 | 复合索引 | 10分钟 | 30分钟 | 轻微 |
| 1000万 | 多键索引 | 15分钟 | 45分钟 | 明显 |
影响因素分析
1 | 索引创建时间影响因素: |
最佳实践总结
线上环境索引创建流程
1 | 1. 评估阶段 |
索引创建检查清单
1 | □ 是否使用了 background: true? |
常见问题
Q1: 后台创建索引为什么还是影响了性能?
原因:
- 后台创建仍需要读取所有文档
- 磁盘 I/O 竞争
- 内存压力增加
优化:
- 选择低峰期执行
- 增加系统资源
- 考虑分片集群分散压力
Q2: 索引创建到 90% 后很慢?
原因:
- 最后阶段需要完成索引构建和验证
- 可能遇到锁竞争
处理:
- 耐心等待,不要强制终止
- 检查当前操作是否有异常
Q3: 如何加速索引创建?
方法:
- 临时增加内存
- 使用 SSD 存储
- 停止不必要的读操作
- 考虑使用前台创建(维护窗口)
总结
MongoDB 大数据量索引创建的要点:
- 必须用后台创建:线上环境一定要加
background: true - 监控创建进度:用
currentOp实时跟踪,心里有数 - 选对执行时机:低峰期执行,减少影响
- 预留足够时间:大数据量可能要几个小时
- 准备应急预案:知道怎么终止和恢复,万一出问题能及时止损
索引创建这东西,看着简单,但大数据量时很容易出问题,提前规划好能省很多麻烦。