본문 바로가기

IT/Database

[mongodb] mongodb에 대한 한탄

mongodb 에 대한 한탄


Kiip 에서 MongoDB 를 이용한지 일년이 되었다. 이번 주에 우리는 MongoDB 의 데이터중 95% 를, 데이터를 이용하는 방식에 맞춰, Riak 이나 PostgreSQL 로 이전하였다. 이 글에서는, 지난 일년간 MongoDB 를 사용해본 경험에 대해서 얘기해보려 한다. 추후에는 어떻게 데이터를 다른 곳으로 옮겼는지에 대한 내용을 다루려고 한다: 데이터를 옮기는 방법들을 어떻게 평가하였고, 실제로 어떻게 MongoDB 에서 다른 곳으로 데이터를 옮겼는지 말이다.

우선, 우리가 다루는 scale 의 감을 잡기 위해 몇가지 숫자들을 열거하겠다. 아래는, 우리가 MongoDB 를 최대로 사용할 때, 가장 사용량이 많을때의 데이터이다 - 현재의 숫자는 훨씬 더 커졌다.

  • Data 크기: 240 GB
  • Document 갯수: 85,000,000
  • 초당 Operation 수: 520 (Create, reads, updates, etc.)

The Good

처음에 MongoDB 에 관심을 가진 이유는 이 website 에서 소개한 기능들과, 몽고디비를 성공적으로 이용한 사람들의 얘기를 듣고서 였다. MongoDB 가 약속하는 기능들을 확인하여 볼 수 있었고, 우리는 MongoDB 를 사용하려고 마음 먹게 되었다.

  • Schemaless - Document data store 로서, schema 가 없다는 것은 MongoDB 의 장점이다. 새로운 필드를 추가하기 쉽고, 모델의 구조를 완전히 바꿔버릴 수도 있다. 작년에 우리는 가장 많이 사용되는 모델들을 몇번이나 변경하였다. 기존의 모든 document 들을 모두 update 하는 대신에, 우리는 단순히 "version" 필드를 document 에 추가하였고, 어플리케이션이 오래된 버전과 새 버전의 document 를 읽어들이도록 하였다. 이런 유연성은 application 개발자와 운영 관리자에게 유용하다.

  • Simple replication - Replica Sets 를 설정하기 쉽고, 잘 동작한다. 나중에 자세히 얘기하겠지만, 초기에는 사용하기 쉽고, 잘 동작하는 것처럼 보인다.

  • Query Language - Querying into Documents 를 할 수 있고 데이터에 대해서 atomic operations 를 수행할 수 있다는 것은 매우 좋다. 이 두가지 기능을 많이 사용하였다. 하지만 불행히도, 이런 query 들은, 내부의 구조적인 문제로 인해, scale 하기가 쉽지 않았다. 초기에는, 고급 query 를 이용하여 우리가 원하는 기능을 쉽게 구현할 수 있었다.

  • Full-featured Drivers for Many Languages - 10gen 은 많은 언어에 대한 official MongoDB drivers 를 보여준다. 우리가 사용한 언어들의 driver 들은 아주 훌륭하였다. MongoDB 를 사용할 때 driver 가 문제가 되는 일은 없었다.

The Bad

MongoDB 가 표면적으로는 많은 좋은 기능들을 가지고 있지만, 대부분은, 내부 구조적 문제들을 안고 있는 경우가 많았다. 이런 문제들을 고칠 수는 있지만, 현재 상태로는 MongoDB 를 사용하기에는 제약이 되는 경우가 많다. 우리가 겪었던 문제점들을 나열해 보겠다.

  • Non-counting B-trees - 몽고디비는 인덱스를 하기 위해 non-counting B-trees 자료구조를 사용한다. MongoDB 를 이용해 할 수 있는 일을 상당히 제한하는 요소이다. index 된 필드의 데이터의 count 를 세려고 하면, Mongo 가 이 데이터들을 B-tree 에서 일일히 방문해야 한다는 것을 의미하기 때문이다. limit/offset 쿼리를 하기 위해, MongoDB 는 B-tree 의 leaf (잎노드) 를 방문해야 (traverse) 한다. 이 불필요한 방문들이, 자주 사용할 데이터를 메모리에서 내리고, 사용하지 않을 데이터를 memory 에 올리게 만든다. 이것에 관하여 2010 년 9 월에 생성된 open ticket 이 있다.

  • Poor Memory Management - MongoDB 는 전체의 데이터를 memory mapped file 로 관리한다. 즉 메모리 관리를 kernel 에게 일임한다. A more intelligent scheme would be able to do things like fault in your indexes before use as well as handle faulting in of cold/hot data more effectively. The result is that memory usage can’t be effectively reasoned about, and performance is non-optimal. (역주: 무슨 소린지, 맞는 얘긴지 잘 모르겠음. 기본적으로는, OS 에 메모리 관리를 맡기기 때문에, DB 에 특화된 메모리 관리를 할 수 없다는 얘기일텐데.. 아시는 분 알려주세요!)

  • Uncompressed field names - "foo" 라는 key 를 가진 1,000 개의 document 를 저장하면, 데이터에 "foo" 가 1,000 번 저장되게 된다. MongoDB 를 이용할 때에는 field 이름을 줄이는 것이 성능상 좋다고 알려져 있다. 이와 관련한 ticket 이 2010 년 4월 부터 open 되어있고, 여전히 문제가 존재한다. Kiip 에서는, model layer 에 이름을 변경하는 기능을 넣어, "username" 이라는 필드가 데이터베이스에 "u" 로 저장되도록 하였다. 사용자 대신에, 데이터베이스가 이름을 변경하여 짧게 만들어주는 것을 자동으로 해주면 좋을 것이다.

  • Global write lock - MongoDB (이 글을 쓰고있는 2.0 버전에서는) process-wide 쓰기 lock 을 사용한다. 개념적으로 말도 안된다고 생각한다. X 데이터에 대한 쓰기가 Y 데이터에 대한 쓰기를 block 할 수 있다는 것인데, MongoDB 는 transaction 이나 join 이란 개념도 지원하지 않는데 왜 이런 제한이 있는지 모르겠다. 하나의 서버당, 초당 200 update 가 한계라는 것을 발견하였다. 이 시점에는, 읽기 작업 을 포함한 모든 작업이 block 된다. 10gen 에 도움을 요청하였을 때, 그들은 sharding 을 제안하였다. 그것이 그들의 scale 방법이기 때문이다. RDBMS 를 사용한다면, 최소한 sharding 을 하기 전에, vertical scaling (역주: table 의 field 를 쪼개는 것, sharding 즉 horizontal scale 은 테이블을 row 로 쪼갠다) 할 수 있는데 말이다.

  • Safe off by default - 벤치마크용으로는 유용할지 몰라도, 기본값으로는 말이 안된다고 생각한다. 비유를 들자면, airbag 을 없애고 자동차를 출시한 후에, 문제가 생겼을 때, "에어백 기능을 킬 수 있었는데 안켰지 않냐" 라고 얘기하는 것이나 마찬가지인 것이다. Kiip 은 원인을 발견하여 safe save 를 user account 나, billing 등에 사용하기 전까지, 꽤나 많은 데이터를 잃어버리는 경험을 하였다.

  • Offline table compaction - MongoDB 의 데이터 크기는 compact the database 를 하기 전까지 무한정 커진다. 그리고 compact 하는 과정은 시간이 오래걸리고 다른 모든 DB 작업을 block 시킨다. 따라서 offline 시에 하던지, secondary/slave 서버에서 수행해야한다. PostgreSQL 같은 RDBMS 서버는 auto-vacuums 기능을 이용하여 데이터베이스를 정리하여 이 문제를 해결하였다.

  • Secondaries do not keep hot data in RAM - 메인 서버가 보조 서버로 (secondary) query 를 보내지 않는다. 따라서 보조 서버가 메모리에 hot data 를 관리하지 않게 된다. 따라서 메인이 망가져서 보조 서버를 이용하게 될때, 데이터를 memory 에 올려야 하는 작업이 필요하다. gigabyte 의 데이터를 메모리에 올리는 작업은 매우 느릴 수 있다. EBS 같은 곳에서 데이터를 가져와야 된다면 더더욱 그렇다. read 를 보조서버로 분산하면 도움이 된다. 하지만, 보조 서버를 backup 이나 failover (역주: 실패시 사용할 서버) 로만 사용한다면, 메인에서 보조서버로 변경할 때에는 데이터를 메모리에 올리는 시간을 고려하기 바란다.

What We’re Doing Now

처 음에는, MongoDB 가 우리가 원하는 유연함과 기능을 제공한다고 생각하였다. 하지만, 구조적인 문제들로 인해, 우리는 다른 해결책들을 찾아봐야 하게 되었다. 빠른 시간안에 MongoDB 를 신뢰할 수 없게 되어, sharding 기능도 사용해보지 않았다. 또한 비교적 작은 데이터의 양을 처리하는데 sharding 이 필요해서는 안된다고 생각했기 때문이다.

지난 6 개월 동안, 우리는 몽고디비에서 data 를 빼네어, MongoDB 를 scale 하였다. 이 과정은 하나의 독립된 포스팅이 필요할 것이다. 하지만 핵심은, 우리가 데이터를 어떻게 사용하는지 살펴보고, 그에 맞는 툴을 고르려고 했다는 것이다. key-value 데이터를 위해선, read/write latency 가 예상가능하고, horizontally scale 가능한 Riak 을 선택하였다. 강력한 query 기능이 필요한 relational data 에 대하여는 PostgreSQL 를 선택하였다. 또 영구저장할 필요가 없는 데이터들은 in-memory solution 으로 관리하고 있다.

돌 이켜보면 MongoDB 는 Kiip 에 맞는 솔루션은 아니었다. 초기에는 좀 더 많은 노력이 들겠지만, PostgreSQL 이나 다른 일반적인 RDBMS 를 먼저 사용하고, 필요에 따라 다른 툴들을 연구해 보기를 추천한다. 추후에는, downtime 을 최소화하면서, 어떻게 데이터를 옮겼는지에 대해서 post 할 예정이다.




출처 : http://codeflow.co.kr/question/992/mongodb-%EC%97%90-%EB%8C%80%ED%95%9C-%ED%95%9C%ED%83%84/