Google AdSense (text)

hidden logo stop

Moving

거지 같은 이글루스 광고노출 정책이 싫어서,
새 보금자리(http://blog.leocat.kr/)로 이사감.

[MongoDB] 새 필드 추가 시 느림 현상 Computer & Program

기존에 사용하던 MongoDB에 새로운 필드를 추가할 일이 생겼다. 이 필드는 처음에는 존재하지 않는 필드지만, 주기적으로 계산돼서 기존의 값이 갱신될 것이다. 함께 맞물린 프로세스가 있어서 얼마나 걸리는지 시간을 확인했다. 그런데 동일한 코드로 동일한 데이터에 대해 실행했는데도 처음 실행할 때와 여러번 실행할 때 시간 차이가 나는 신기한 현상이 있었다.

비슷한 상황의 예제를 만들었다.
#!/usr/bin/env groovy

@Grab('com.gmongo:gmongo:1.0')

import com.gmongo.GMongo

// 전달인자, 'init', 'update', 'clean' 등 실행할 동작 명령을 받는다.
// init - collection에 초기 데이터 적재
// update - 새 필드 추가 또는 수정
// clean - collection 제거
def op = args[0]
def startTime = System.currentTimeMillis()

def mongo = new GMongo('localhost', 27017)
def db = mongo.getDB('test')  // test DB
def collection = db.test      // test collection

def rand = new Random()
for(i in 1..1000000) {
        // 초기 데이터 적재
        if('init'.contentEquals(op))
                collection.insert([
                        name: "name${i}",
                        age: rand.nextInt(100)])
        // 'sex' 필드 추가/수정 (update)
        if('update'.contentEquals(op))
                collection.update(
                        [name: "name${i}"],
                        [$set: [sex: "${rand.nextBoolean()? 'M' : 'F'}"]])
}
// 초기 데이터 적재 시 index 생성
if('init'.contentEquals(op))
        collection.ensureIndex([name:1])
// collection 제거
if('clean'.contentEquals(op))
        collection.drop()

println "Elapsed: ${System.currentTimeMillis() - startTime}ms"

groovy 코드지만 java와 비슷하기 때문에 이해하는데는 큰 무리가 없을 것이다. 전달인자로 'clean'을 주면 'test' DB의 'test' collection을 제거한다. 'init'을 주면 아래와 같이 생긴 초기 데이터를 1000000건 추가한다. 'update'를 주면 기존의 문서에서 'sex'라는 성별 필드를 update한다. 그 결과는 아래와 같을 것이다. 처음에는 'init' 시에는 필드가 존재하지 않고, 'update'를 실행하면 필드가 추가될 것이다. 그리고 또 다시 'update'를 하면 기존 필드의 내용이 (random하게) 변경될 것이다.

$ mongo
> use test
> // 'init'한 다음 상태
> db.test.findOne({name: 'name123'})
{
    "_id" : ObjectId("5109c5d5e4b088e4064daf98"),
    "name" : "name123",
    "age" : 12
}
> // 'update'한 다음 상태 - 'sex'가 추가됨
> db.test.findOne({name: 'name123'})
{
    "_id" : ObjectId("5109c5d5e4b088e4064daf98"),
    "age" : 12,
    "name" : "name123",
    "sex" : "F"
}
> // 다시 'update'한 다음 상태 - 'sex'가 다시 세팅됨('F' -random-> 'M')
> db.test.findOne({name: 'name123'})
{
    "_id" : ObjectId("5109c5d5e4b088e4064daf98"),
    "age" : 12,
    "name" : "name123",
    "sex" : "M"
}

그러면 각 명령을 실행할 때 마다 걸린 시간을 확인해 보자.

$ ./mongoTest.groovy clean
Elapsed: 243ms
$ ./mongoTest.groovy init
Elapsed: 39352ms
$ ./mongoTest.groovy update
Elapsed: 79301ms
$ ./mongoTest.groovy update
Elapsed: 32143ms
$ ./mongoTest.groovy update
Elapsed: 32617ms
$ ./mongoTest.groovy update
Elapsed: 32342ms
$ ./mongoTest.groovy update
Elapsed: 32619ms
$ ./mongoTest.groovy clean
Elapsed: 242ms

처음 update 때는 79초가 걸렸지만, 다음에는 32초 밖에 걸리지 않는 것이다. 몇 번을 해봐도 동일하다.

일단 예상하기로는, Mongo 문서에 없던 필드를 추가하면 저장공간을 확보하고, 문서에 포함된 내용임을 저장하는 등 여러 부수적인 작업이 필요하다. 하지만 그 다음에는 이미 존재하는 필드의 값을 바꾸기만 하면 되기 때문에 빠른 것이 아닐까..?? 하는 질문만 던지고 오늘은 여기서 퇴장.. -ㅅ-;;;

덧글

  • 환상법사 2013/02/01 11:55 # 삭제 답글

    groovy라면 op가 init인지 update인지 판단하는 방법으로 switch를 쓸 수도 있었을 텐데요.
    아니 그보다 groovy니까 클로저를 사용할 수도 있었을텐데요.
  • Sigel 2013/02/01 15:20 #

    오~ 클로져.. 그거 재밌겠네요.. 역시 너무 머리가 굳어져서 창의력이 없어졌어요
댓글 입력 영역

Google AdSense (text/image)