05-19 03:10
Notice
Recent Posts
Recent Comments
관리 메뉴

Scientific Computing & Data Science

[MongoDB] Query / skip 속도 높이기 본문

Data Science/MongoDB

[MongoDB] Query / skip 속도 높이기

cinema4dr12 2014. 2. 6. 23:41

Written by cinema4d

앞서 "Cursors"를 다룬 글에서 도큐먼트 쿼리 시 "skip"을 통해 도큐먼트를 건너뛰는 것에 대해 알아본 바 있다.

"skip" 연산자는 작은 규모의 도큐먼트에 대해서는 적당할 지 모르지만, 도큐먼트 규모가 커지면 건너뛰는 속도가 현저히 떨어진다. 이것은 MongoDB만의 문제가 아닌 거의 모든 데이터베이스에서 공통적으로 나타나는 현상이다.

따라서, 큰 규모의 도큐먼트에서 도큐먼트를 건너뛰어 검색 결과를 받아올 때 "skip"은 반드시 피해야 한다.


skip 없이 결과 표시하기

다음과 같이 1 ~ 30000까지의 "cnt" 값을 가지는 도큐먼트를 생성해 보자:

for(i=1; i<30001; i++) {
	db.count.insert({cnt : i});
}


만약 20000개의 도큐먼트를 건너뛰려면

var myCursor = db.count.find().skip(20000);

대신 다음과 같이 "$gt" 연산자를 사용하는 것이 훨씬 빠른 속도를 보일 것이다(상황에 따라 속도차이를 거의 못 느낄 수도 있다.)

> var myCursor = db.count.find({cnt: {$gt: 20000}}); > myCursor { "_id" : ObjectId("52f38fff2d911bccacf26913"), "cnt" : 20001 } { "_id" : ObjectId("52f38fff2d911bccacf26914"), "cnt" : 20002 } { "_id" : ObjectId("52f38fff2d911bccacf26915"), "cnt" : 20003 } { "_id" : ObjectId("52f38fff2d911bccacf26916"), "cnt" : 20004 } { "_id" : ObjectId("52f38fff2d911bccacf26917"), "cnt" : 20005 } { "_id" : ObjectId("52f38fff2d911bccacf26918"), "cnt" : 20006 } { "_id" : ObjectId("52f38fff2d911bccacf26919"), "cnt" : 20007 } { "_id" : ObjectId("52f38fff2d911bccacf2691a"), "cnt" : 20008 } { "_id" : ObjectId("52f38fff2d911bccacf2691b"), "cnt" : 20009 } { "_id" : ObjectId("52f38fff2d911bccacf2691c"), "cnt" : 20010 } { "_id" : ObjectId("52f38fff2d911bccacf2691d"), "cnt" : 20011 } { "_id" : ObjectId("52f38fff2d911bccacf2691e"), "cnt" : 20012 } { "_id" : ObjectId("52f38fff2d911bccacf2691f"), "cnt" : 20013 } { "_id" : ObjectId("52f38fff2d911bccacf26920"), "cnt" : 20014 } { "_id" : ObjectId("52f38fff2d911bccacf26921"), "cnt" : 20015 } { "_id" : ObjectId("52f38fff2d911bccacf26922"), "cnt" : 20016 } { "_id" : ObjectId("52f38fff2d911bccacf26923"), "cnt" : 20017 } { "_id" : ObjectId("52f38fff2d911bccacf26924"), "cnt" : 20018 } { "_id" : ObjectId("52f38fff2d911bccacf26925"), "cnt" : 20019 } { "_id" : ObjectId("52f38fff2d911bccacf26926"), "cnt" : 20020 } Type "it" for more


랜덤 도큐먼트 검색하기

컬렉션으로부터 랜덤 도큐먼트를 얻는 방법 중 가장 단순하면서도 무식한(?) 방법은 전체 도큐먼트 수를 파악하고 이 범위 안에서 랜덤 수를 발생시켜 그 수만큼 도큐먼트를 "skip"하는 것이다. 다음 예를 살펴보자.

for(i=1; i<1001; i++) {
	db.myCollection.insert({cnt : i});
}
var total = db.myCollection.count();
var random = Math.floor(Math.random()*total);
db.myCollection.find().skip(random).limit(1);

위의 코드는 1000개의 도큐먼트를 생성하고, 도큐먼트의 전체 개수를 파악하여 전체 개수의 범위에 대한 랜덤 수를 발생시킨 후, 그 수만큼 "skip"한 후 1개의 도큐먼트를 얻어왔다.

그러나, 이 방법은 도큐먼트 수가 매우 많을 때에는 속도가 현저히 느려지므로 좋은 방법이 아니다. 일반적으로 사용하는 랜덤 도큐먼트 검색은 약간의 트릭이 숨어있다.

위의 myCollection에는 키(key) 값이 "cnt" 뿐인데, 각 도큐먼트에 랜덤 번호를 추가하여, 랜덤 수를 발생하여 그 수보다 작거나 큰 하나의 도큐먼트를 찾는 방법이 훨씬 효율적이다.

다음 예를 통해 보다 효율적인 "skip" 방법을 알아보자.

// myCollection에 "random" 키를 설정하고 랜덤수를 저장한다.
for(i=1; i<1001; i++) {
	db.myCollection.update({cnt: i}, {$addToSet: {random: Math.random()}});
}

// 랜덤수를 하나 발생시키고 그 값보다 큰 도큐먼트 1개를 찾는다.
var myRandom = Math.random();
result = db.myCollection.findOne({random : {$gt : myRandom}});

// 만약 "result"에 아무값도 저장되지 않으면, 발생된 랜덤수보다 작은 도큐먼트 1개를 찾는다.
if (result == null) {
	result = db.myCollection.findOne({random : {$lt : random}});
}

라인 10 - 13의 설명은 다음과 같다: 랜덤수를 하나 발생시키고 그 값보다 큰 도큐먼트 1개를 찾는 경우, 발생된 랜덤수보다 큰 "random" 값을 가지고 있는 도큐먼트가 없을 확률도 있다.

이를 방지하기 위해 "result"에 아무 값도 저장되어 있지 않다면(result == null) 좀전에 발생한 랜덤수보다 작은 도큐먼트 1개를 찾는다.

'Data Science > MongoDB' 카테고리의 다른 글

[MongoDB] Query / $snapshot  (0) 2014.02.09
[MongoDB] Query / Other Query Operations  (0) 2014.02.08
[MongoDB] Query / Cursors  (0) 2014.02.06
[MongoDB] Query / $where  (0) 2014.02.04
[MongoDB] Query / Querying Embedded Documents  (0) 2014.02.02
Comments