有一个错题本统计功能需求,当前我的做法是用mongodb一个集合 exercise 来保存做题记录,每做一道题就保存一条记录,数据用aggregate进行聚合统计。目前exercise 集合已经有2000多万记录,而且增长速度很快,一条记录结构如下:
{
"_id" : ObjectId("58856c54c10da3925edc9a4b"),
"eid" : NumberLong(1), // 试题ID
"right" : NumberLong(1), // 是否正确
"scene" : NumberLong(1), // 场景
"uid" : NumberLong(663148), // 用户
"total" : NumberLong(1), // 试题总分
"score" : NumberLong(1), // 用户得分
"rate" : NumberLong(100), // 正确率
"date" : ISODate("2017-01-23T12:17:00.000Z"), // 做题时间
"subject" : NumberLong(1), // 科目
"class" : NumberLong(1), // 班级
"grade" : NumberLong(1), // 年级
"school" : NumberLong(1), // 学校
"tags" : [
{
"tagID" : NumberLong(39544), // 章节ID
"level" : NumberLong(1) // 章节层级
},
{
"tagID" : NumberLong(39621),
"level : NumberLong(1)
}
]
}
需求如下:
(1)根据年级,科目,日期,学校查找错题列表,并且每一道错题要统计做错次数
(2)根据年级,科目,学校,统计每个场景错题数目
(3)根据年级,科目,教材统计教材下各个章节对应所有错题做错总次数
目前对学校加了索引,但当学校数据量有几百万时,对于以上需求查询速度很慢,不知道其他大网站是怎么设计的,查询速度很快。求有经验的大神赐教
首先是索引的问题,你的三个查询对应的三个索引分别是:
但是索引并不能完全解决你的问题。心里要有一个概念,凡是涉及到统计的问题,涉及的数据量越多,速度越慢,毕竟你是要一次读取这么多数据,其速度肯定是无法和查询几条记录的速度相比的。
但是练习结果是随着人做不断增多的,如果每次都对全部数据做一次统计,数据量越来越大,势必越来越慢。所以问题变成如何控制每次统计的数据数量?以下两个方面都应该有所考虑:
预聚合是很常见的处理时序数据增长的手段。以第一个问题为例:
假设每天有10道这样的错题,100天过去就会有1000道。这时候你的查询一次要统计1000条记录才能得到结果;
如果我每天结束时按天做预聚合,100天过去后我就只有10条记录,里面记录了每天的统计结果。这时候就只需要把这100条记录的结果汇总,就是我要的结果;
进一步,如果有必要还可以考虑月、年等聚合粒度,这样在跨长时间的查询中就会有更多优势。