如何解决MongoDB出现Query Targeting扫描/返回比超1000的告警
问题根因
你遇到的告警核心原因是MongoDB查询优化器选择了非最优的updated_at_1单字段索引,导致查询需要扫描所有符合updated_at >= 2021-09-24的60万+文档,再逐行过滤key和$or条件,匹配到的待推送文档越少,扫描/返回比值就越高,最终触发阈值告警。
你之前的方案未生效的原因:
- 新增的
{"updated_at": 1, "key": 1}索引将范围过滤字段updated_at放在了等值过滤字段key前面,等值过滤无法有效缩小索引扫描范围,优化器评估其效率和原单字段索引差异不大,所以未选择。 aggregate的$match阶段和find的查询优化逻辑完全一致,没有适配索引的前提下不会改变索引选择结果。- 原有复合索引
updated_at_1_a_has_sent_1_b_has_sent_1未包含查询必需的key过滤字段,无法覆盖所有查询条件,优化器评估成本更高因此未选择。
解决方案
1. 新建最优匹配索引
按照「等值过滤在前、范围过滤在后、覆盖所有过滤条件」的复合索引设计原则,创建如下索引:
{"key": 1, "updated_at": 1, "a_has_sent": 1, "b_has_sent": 1}
该索引可以直接在索引层面完成所有条件过滤:先定位key="key_1"的所有文档,再匹配updated_at的时间范围,最后过滤a_has_sent/b_has_sent的条件,不需要扫描额外文档,扫描/返回比值可以降到1左右。
2. 强制指定索引(可选)
如果优化器仍因历史统计信息偏差选择旧索引,可以在查询时通过hint强制指定新索引:
db.collectionname.find({ "key": "key_1", "updated_at": { "$gte": ISODate("2021-09-24")}, "$or": [{ "a_has_sent": false }, {"b_has_sent": false}] }).hint("key_1_updated_at_1_a_has_sent_1_b_has_sent_1")
你也可以先执行db.collectionname.stats()或者db.collectionname.reIndex()更新集合的统计信息,帮助优化器自动选择正确索引。
3. 清理冗余索引(可选)
确认新索引生效后,可以删除之前创建的未生效的{"updated_at": 1, "key": 1}索引,降低集合写入时的索引维护开销。
内容的提问来源于stack exchange,提问作者zangw
相关产品推荐
相关产品推荐

