具体到这次调优,是一个关于mongoDB的分页问题。在分页时如果用skip,在页数比较大时,性能下降得非常厉害。
具体语句是db.foo.find().sort().skip().limit();mongo官方也明确说不建议用skip做分页。
做分页的标准做法是,记住上一个页的最后一个值,然后取这个值之后的100条记录,这样回避skip。具体语句如下:
var latest = null;
//显示第一页
while(page1.hasNext()) {
latest = page1.next(); //记住当前对象,在循环结束时,这个对象就是这页的最后一个对象
display(latest); //送到前台展示
}
// 获取下一页,假设表名是foo,按date进行排序,$gt是大于的意思
var page2 = db.foo.find({"date":{"$gt":latest.date}}); //取时间大于latest.date的数据
page2.sort({"date":-1}).limit(100); //在游标page2中取头100行记录。
这样就避免使用skip了。这是标准做法。
但即使这样的做法,数据量特别大时,性能也不好,尤其是用户直接点取最后一页时。
我建议在最后几页时,采用和取头几页相反的顺序。例如正常取评论时,是按时间逆序的,如果用户要看最后一页,就按时间正序取数据就好了,当然具体到产品,还有一些条件分支什么的,这些是宝龙自己细化的。
这种与正常逆序的取法,实际不是我自己想出来的,网上有一篇关于mongo调优的博文,最后一段提到了这种做法。
很好的一篇文章。
案例
//统计img
public List<VideoEncode> doStaticImg() {
List<VideoEncode> list = new ArrayList<VideoEncode>();
int pageSize = 100;
long count = videoEncodeDao.getCount(startTime, endTime);
long pageCount = count % pageSize == 0 ? count / pageSize : count / pageSize + 1;
long createTime = startTime;
for (int i = 0; i < pageCount; i++) {
List<VideoEncode> videoEncodeList = videoEncodeDao.getAll(createTime, endTime, pageSize);
if (videoEncodeList.isEmpty()) break;
createTime = videoEncodeList.get(videoEncodeList.size()-1).getCreateTime();
List<VideoEncode> imgFailList = getImgPathFail(videoEncodeList);
if (!imgFailList.isEmpty()) list.addAll(imgFailList);
}
return list;
}
//获取指定时间转码、分发,checksum完成的任务
public List<VideoEncode> getAll(long startTime, long endTime,int count){
Query <VideoEncode> query=createQuery();
query.field("status").equal(VideoEncode.STATUS_COMPLETE).field("videoCDNStatus").equal(VideoEncode.CDN_STAUTS_COMPLETE);
query.field("checksumStatus").equal(VideoEncode.CDN_STAUTS_COMPLETE).field("mmsStatus").equal(VideoEncode.MMS_STATUS_SYNCED);
query.field("vtype").equal("1").field("createTime").greaterThan(startTime).field("createTime").lessThan(endTime).order("encodeId");
query.limit(count);
QueryResults <VideoEncode> queryResults=find(query);
return queryResults.asList();
}
//获取指定时间转码、分发,checksum完成的任务数量
public long getCount(long startTime, long endTime){
Query <VideoEncode> query=createQuery();
query.field("status").equal(VideoEncode.STATUS_COMPLETE).field("videoCDNStatus").equal(VideoEncode.CDN_STAUTS_COMPLETE);
query.field("checksumStatus").equal(VideoEncode.CDN_STAUTS_COMPLETE).field("mmsStatus").equal(VideoEncode.MMS_STATUS_SYNCED);
query.field("vtype").equal("1").field("createTime").greaterThan(startTime).field("createTime").lessThan(endTime);
return count(query);
}