此查询在我的应用程序中花费了很多时间。数据库包含大约200万条记录。
$results = $queryBuilder
->field('extra.targetPlayerId')->exists(FALSE)
->field('permission')->equals('public')
->field('time')->gt($weekEndTime) // variable is timestamp
->field('time')->lte($startTime) // variable is timestamp
->map(
'function() {
var total = 0;
if (this.comments) {
total += this.comments.length;
}
if (this.likes) {
total += this.likes.length;
}
if (total > 0) {
emit(this.playerId, total);
}
}'
)
->reduce(
'function(key, values) {
var total = 0;
for (value in values) {
total += values[value];
};
return total;
}'
)->getQuery()->execute();
如何优化此查询?你能给我建议索引吗?
答案 0 :(得分:4)
->field('extra.targetPlayerId')->exists(FALSE)
这可能是你加快查询速度的最大障碍。
来自the docs:
在v2.0之前,$ exists无法使用索引。仍然使用其他字段的索引。 $存在即使使用索引也不是很有效,尤其是使用{$ exists:true},因为它实际上必须扫描所有索引值。
也许考虑添加一个布尔字段,以便您可以测试true / false而不是exists - 并在索引中包含该字段。
甚至可以将特定条件移动到地图功能中:
function() {
var total = 0;
if (!this.extra || !this.extra.targetPlayerId) {
return; //bail - count nothing
}
if (this.comments) {
total += this.comments.length;
}
if (this.likes) {
total += this.likes.length;
}
if (total > 0) {
emit(this.playerId, total);
}
}
索引不是黑白答案,例如,如果您有大量数据:
{
time: 1,
permission: 1,
extra.hasTargetPlayer: 1
}
可能会运作得很好 - 因为它可以让mongo专注于正确的日期范围作为第一个标准。
除此之外,简单地更改数据模式可能更合适。您是否存储该查询的结果?
即。查询一次并存储结果,以便您可以执行任何后续请求:
db.appstats.findOne({_id: "201152"})
其中_id是年份和周数。如果您按周,每周生成统计数据 - 您可以按计划(cron)执行繁重的查询,因此用户不会受查询速度的影响,无法实际计算结果。