MongoDB数据重组查询/操作执行过慢,如何优化提速?
MongoDB 数据规范化脚本提速方案(非多线程)
1. 优先补全缺失索引(性能提升最明显)
你当前脚本的绝大多数耗时都来自无索引的全表扫描,加对索引可以直接把执行时长从5天压缩到小时级:
- 给
Interest集合加部分复合索引,仅索引你需要处理的符合条件的文档,占用空间小、查询效率极高:
db.Interest.createIndex( {PersonID: 1, 'Data.DateOfBirth': 1}, {partialFilterExpression: {'PersonID': {$exists: false}, 'Data.DateOfBirth': {$ne: null}}} )
- 给
People集合加唯一复合索引,匹配你查询人员的三个过滤字段,同时避免插入重复人员:
db.People.createIndex({Name: 1, BirthMonth: 1, BirthYear: 1}, {unique: true})
2. 批量操作减少数据库交互次数
你现在每条记录对应2-3次单独的数据库请求,1000万条会产生数千万次请求开销,改成批量处理即可大幅降低 overhead:
- 去掉多余的
toArray()调用,直接用游标迭代结果,避免一次性加载全量数据撑爆内存:把.toArray().forEach()改为直接.forEach()。 - 攒够1000~5000条操作后用
bulkWrite接口批量提交,不要每条单独发updateOne/insertOne,可以减少99%的网络和请求解析开销。 - 用
upsert逻辑代替先查后插,省掉每次查询People的开销:不需要先调用findOne判断人员是否存在,直接用如下写法一次请求就能拿到人员ID:
// 一次请求完成查找/插入,省掉一次findOne查询 const upsertRes = db.People.updateOne( peep, {$setOnInsert: peep}, {upsert: true} ) const personId = upsertRes.upsertedId || upsertRes.matchedId
3. 冗余逻辑裁剪
- 你查询
Interest的过滤条件已经加了'Data.DateOfBirth': {$ne: null},循环内的if (null != x.Data.DateOfBirth)判断完全冗余,可以直接删除。 - 降低打印频率,每1000条打印一次的操作会拖慢脚本执行速度,建议改成每1万甚至10万条打印一次进度即可。
4. 写入配置调优
如果是副本集部署,可适当调低写入关注级别,不需要等待副本节点同步确认即可返回写入成功:执行操作时指定writeConcern: {w: 1},如果可接受极端场景下极小概率的数据丢失,可进一步设置为{w: 0, j: false},写入速度会更快。
内容的提问来源于stack exchange,提问作者Ian Newson
相关产品推荐
相关产品推荐

