1. 程式人生 > 實用技巧 >mongodb系列(一)mongoose find普通查詢與aggregate聚合查詢的 簡單效能對比

mongodb系列(一)mongoose find普通查詢與aggregate聚合查詢的 簡單效能對比

背景

mongodb 3.4,使用預設的配置(沒有調整記憶體限制或其他效能配置),資料庫有90萬+條資料

開始實驗

以下分別使用 find 查詢 和 aggregate 聚合查詢

以下為dao層(server/dao/cmsResourceDao.js),基於mongoose查詢mongodb

  1. getModel(){
  2. return mongoose.model(this.model_name); //model_name 為collection名稱
  3. }
  4. /***
  5. * 使用find 查詢,並且是分頁查詢
  6. * @param limit_param 條件
  7. * @param page_index 資料頁碼起始下標
  8. * @param page_size 資料每頁數量
  9. * @param sort 排序條件
  10. * @returns {Promise.<{rows: *, total_count: *}>}
  11. */
  12. async listPage(limit_param,page_index,page_size,sort){
  13. let rows;
  14. if(sort != null){
  15. rows = await this.getModel().where(limit_param).skip(Number(page_index)).limit(Number(page_size)).sort(sort).exec();
  16. }else{
  17. rows = await this.getModel().where(limit_param).skip(Number(page_index)).limit(Number(page_size)).exec();
  18. }
  19. return {rows};
  20. }
  21. /***
  22. * 使用aggregate聚合 查詢,並且是分頁查詢
  23. * @param limit_param 條件
  24. * @param page_index 資料頁碼起始下標
  25. * @param page_size 資料每頁數量
  26. * @param sort 排序條件
  27. * @returns {Promise.<{rows: *, total_count: *}>}
  28. */
  29. async listAggregatePage(limit_param,page_index,page_size,sort){
  30. let aggregate_limit = [{$match:limit_param},
  31. {$skip:Number(page_index)},
  32. {$limit:Number(page_size)}];
  33. if(sort != null)aggregate_limit.push({$sort:sort});
  34. let rows = await this.getModel().aggregate(aggregate_limit);
  35. return {rows};
  36. }

1.find查詢和aggregate查詢(不使用sort排序)

以下為使用find查詢50000條資料返回:

  1. async list(ctx,next){
  2. console.log('list!!!');
  3. let type = ctx.request.query.type;
  4. let page_index = ctx.request.query.page_index;
  5. let page_size = ctx.request.query.page_size;
  6. let where = {};
  7. if(type != null)where.type = type;
  8. let start_time = new Date().getTime();
  9. let result = await dao.listPage(where,page_index,page_size);
  10. // let result = await dao.listAggregatePage(where,page_index,page_size); //使用聚合查詢方式
  11. let end_time = new Date().getTime();
  12. console.log('查詢時間:');
  13. console.log(end_time - start_time);
  14. ctx.body = {
  15. data : result,
  16. time : new Date().getTime() - start_time
  17. }
  18. }

列印的時間是:8504ms

使用aggregate查詢50000條資料返回:

  1. async list(ctx,next){
  2. console.log('list!!!');
  3. let type = ctx.request.query.type;
  4. let page_index = ctx.request.query.page_index;
  5. let page_size = ctx.request.query.page_size;
  6. let where = {};
  7. if(type != null)where.type = type;
  8. let start_time = new Date().getTime();
  9. // let result = await dao.listPage(where,page_index,page_size);
  10. let result = await dao.listAggregatePage(where,page_index,page_size); //使用聚合查詢方式
  11. let end_time = new Date().getTime();
  12. console.log('查詢時間:');
  13. console.log(end_time - start_time);
  14. ctx.body = {
  15. data : result,
  16. time : new Date().getTime() - start_time
  17. }
  18. }

列印的時間是:2241ms

2.find查詢和aggregate查詢(使用sort排序)

使用sort排序挑戰效能極限

在剛才使用find查詢,新增sort條件

let result = await dao.listPage(where,page_index,page_size,{updated_at:-1});

結果控制檯報出,記憶體超出限制(最大值為 33554432 bytes 摺合為 32mb左右):

service error { MongoError: Executor error during find command: OperationFailed: Sort operation used more than the maximum 33554432 bytes of RAM. Add an index, or specify a smaller limit.

使用aggregate查詢,新增sort條件

let result = await dao.listAggregatePage(where,page_index,page_size,{updated_at:-1}); //使用聚合查詢方式

列印的時間是:2298ms

那麼aggregate查詢的記憶體最大值究竟有多少呢?再玩大的,這次查詢10萬條

service error { MongoError: Sort exceeded memory limit of 104857600 bytes, but did not opt in to external sorting. Aborting operation. Pass allowDiskUse:true to opt in.

由此可見 aggregate對排序也是有記憶體限制的(最大值為104857600 bytes摺合為100mb左右)

總結

從查詢的速度看,aggregate效率更勝一籌。

從記憶體限制看,aggregate比find更高一點。

從上述實驗中,aggregate 好像比 find 查詢 更勝一籌,但並不意味著 aggregate就是最好的,初步判斷這是由於aggregate更消耗記憶體換取查詢的速度。下一集,再深層次挖掘兩者區別

PS: 原始碼已提交到github

https://github.com/rcjjian/big_data_lab