2016-06-07 2 views
2

안녕하세요, Node.js를 : 점점 async.map 느린

나는 MongoDB를 데이터베이스에 데이터를 저장하기위한 API를 제공하기 위해 Node.js를 사용합니다.

read 메서드에서 여러 테스트를 실행했습니다.이 테스트에서는 ID를 사용하고 해당 문서를 반환합니다. 요점은이 문서를 지정된 순서대로 반환해야한다는 것입니다. 이를 보장하기 위해 다음 코드를 사용합니다.

// Sequentially fetch every element 
function read(ids, callback) { 
    var i = 0; 
    var results = []; 
    function next() { 
     db.findOne(ids[i], function (err, doc) { 
      results.push(err ? null : doc); 
      if (ids.length > ++i) { 
       return next(); 
      } 
      callback(results); 
     }); 
    } 
    next(); 
} 

이렇게하면 올바른 순서로 문서를 하나씩 가져옵니다. 27k 문서를 검색하려면 노트북에서 약 11 초가 걸립니다. 나는 27K 문서는 간단한 코드를 사용하여 8 초 만에 검색 한 것을보고 매우 만족, 단일 테스트를 실행 한 후

// Asynchronously map the whole array 
var async = require('async'); 

function read(ids, callback) { 
    async.map(ids, db.findOne.bind(db), callback): 
} 

:

그러나, 나는이 방법을 개선 할 수있다라고 생각했습니다.

동일한 요청을 반복하면 문제가 발생합니다. 응답 시간은 계속 증가합니다 (검색된 요소 수에 비례하여) : 9s 10s 11s 12s.... 이 문제는 순차 버전에서는 발생하지 않습니다.

두 버전의 Node.js, v6.2.0 및 v0.10.29를 사용해 보았습니다. 문제는 동일합니다. 이 대기 시간의 원인은 무엇이며 어떻게 억제 할 수 있습니까?

+1

왜 '찾기'를 사용하지 않습니까? –

+0

여기를 참조하십시오 : http://stackoverflow.com/questions/8303900/mongodb-mongoose-findmany-find-all-documents-with-ids-listed-in-array –

+1

'async.mapLimit'을 사용하여 과부하를 방지하십시오. 그러나'find ({_ id : {$ in : list}})'는 여러 번이 아니라 단일 데이터베이스 요청 때문에 항상 더 좋습니다. –

답변

4

과부하를 방지하려면 async.mapLimit을 사용해보십시오. 환경에 한계 값을 조정하려면 몇 가지 테스트가 필요합니다.


그러나 find({_id: {$in: list}})는 항상 더 나은 하나의 데이터베이스 요청 대신 여러 때문이다.

원래 주문 클라이언트 측 복원을 시도하는 것이 좋습니다. 이 같은
뭔가 :

function read(ids, cb) { 
    db.find(
    {_id: {$in: ids.map(id => mongoose.Types.ObjectId(id))}}, 
    process 
); 

    function process(err, docs) { 
    if (err) return cb(err); 
    return cb(null, docs.sort(ordering)) 
    } 
    function ordering(a, b) { 
    return ids.indexOf(b._id.toString()) - ids.indexOf(a._id.toString()); 
    } 
} 

이 될 쿼리를 찾을 수 있습니다 내가 정확한 MongoDB를 드라이버를 사용하는지 알고 수 없습니다, 수정 될 필요가있다.

이 코드는 먼저 시도하는 것이므로 수동 정렬을 사용하면 성능을 많이 향상시킬 수 있습니다. [].indexOf도 무겁습니다 (O(n)).
하지만 거의 일지라도 지금은 더 빨리 작동합니다.


가능한 ordering 교체 :

var idHash = {}; 
for(var i = 0; i < ids.length; i++) 
    idHash[ids[i]] = i; 
function ordering(a, b) { 
    return idHash[b._id.toString()] - idHash[a._id.toString()]; 
} 

모든 정렬 알고리즘은 최상의 경우에 O(nlogn)이 있지만, 우리는 이미 우리가 O(n)에 의해 원래 순서를 복원 할 수 있습니다, 발견 된 각 문서의 위치를 ​​결과를 알고, 그래서 :

var idHash = ids.reduce((c, id, i) => (c[id] = i, c), {}); 
function process(err, docs) { 
    if (err) return cb(err); 
    return cb(null, 
    docs.reduce(
     (c, doc) => (c[idHash[doc._id.toString()]] = doc, c), 
     ids.map(id => null))) //fill not_found docs by null 
} 

기능적 스타일은 코드를 유연하게 만듭니다. 예를 들어이 코드는 async.reduce을 사용하여 동기화 차단을 줄이기 위해 쉽게 수정할 수 있습니다.

+1

감사! 나는 당신의'find' 솔루션을'idHash' 객체와 함께 사용했지만, 새로운 배열 (in-place sorting보다 시간 효율적이고 공백 값을 채울 필요가 있습니다)에서 사용했습니다. 'async.mapLimit' 솔루션만큼 메모리가 효율적이지는 않지만 ~ 7s 대신에 ~ 1s 응답 시간을 제공합니다 (27kid의 테스트 배열 사용). – GnxR

+0

그래, 각 요소 위치를 이미 알고 있다면 실제로 정렬 할 필요가 없습니다. –

+0

@ GnxR, 기능적 스타일로 구현하는 방법을보세요 :) –