데이터실무

DA, SQL, DB보안 등 실무자를 위한 위한 DB기술 바이블!

MongoDB

데이터 저장
분산데이터베이스(NoSQL)
MongoDB
작성자
admin
작성일
2021-02-15 13:48
조회
3311

MongoDB는 관계형 데이터베이스가 아닌 문서(document) 지향 데이터베이스다. 문서 지향 데이터베이스에서는 행(row) 대신 문서(document)의 개념을 사용한다. 문서와 배열의 개념은 MongoDB가 복잡한 계층 관계를 하나의 레코드로 표현하기에 용이하도록 만들어 주었다. 이러한 장점은 개발자들이 MongoDB를 더 편리하고 적합한 모델로 주목하게 만들었으며, 꾸준한 release가 이뤄지고 있어 지속적인 발전이 기대되고 있다. 10gen에서(현재는 사명이 MongoDB로 변경됨) PaaS의 구성 요소로 처음 개발한 이후 지금은 다양한 서비스의 백엔드 소프트웨어로 채택되기에 이르렀다.


MongoDB Release별 특징

MongoDB는 일정 주기별로 신규 버전을 발표하고 있으며, 초기의 1.x 버전을 넘어 현재는 2.6.6까지 release됐다(2014년 12월 31일 기준, 2.6.6이 stable이며, 2.8.x 버전은 bug hunting을 수행중이다). release별로 모두 의미있는 변화가 있었지만, 2.2 버전 이후부터 기능, 성능, 안정성 측면에서 큰 발전을 이루었다. 이번 장에서는 2.2, 2.4, 2.6의 release별 특징을 살펴 보자.


MongoDB 2.2
집계 프레임워크

MapReduce를 사용하지 않아도, 집계(Aggregation) 연산을 가능하게 하는 집계 프레임워크가 도입됐다. Data Processing Pipeline 개념을 모델로 한 방식이며, 아래는 aggregation-pipeline의 예다.

[그림 Ⅱ-2-9] Aggregation의 예

[그림 Ⅱ-2-9] Aggregation의 예


TTL Collections

TTL Collections는 매분마다 만료된 도큐먼트를 지우는 백그라운드 스레드와 특정 인덱스를 이용해 컬렉션에서 만료된 데이터를 제거한다. 몇몇 상황에서 capped 컬렉션의 대안으로 유용하다.


동시성 향상(Concurrency Improvements)

2.2버전에서는 동시적인 연산(operation)이 향상됐으며, 이는 아래 항목들의 향상에 기인한다.


  • DB Level Locking
  • Page Faults 시에 양보정책의 향상
  • Page Fault 탐지 향상
MongoDB 2.4
해시 기반 샤딩

기존 Shard Key + Auto-Sharding 방식에서 Shard Key를 해싱해 사용하는 해시 기반 샤딩(Hash-Based Sharding)으로 변경됐다.


리플리케이션 개선

2.4 release에서는 리플리케이션 성능의 개선과 안정성이 개선됐으며, 이는 2.4 release의 주요 특징이기도 하다.


V8 Javascript 엔진

기존의 Javascript 엔진을 SpiderMonkey에서 V8로 교체했다. 이로써 mapreduce, group, eval등의 기능 향상을 기대할 수 있게 했다.


Faster Counts

기존의 Count operation과 달리 2.4 release에서는 count 시에 전체 document에 접근하지 않는다. 기존 버전에 비해 최소 20배 이상의 속도 향상을 가져왔다고 소개하고 있다.


MongoDB 2.6
쿼리 엔진 개선

하나 이상의 인덱스를 사용하는 쿼리에서 교차 인덱스(Index Insectiion)를 수행할 수 있다. 인덱스 필터(Index Filters)는 어떤 인덱스가 특정 쿼리에 대해 winning plan이 될 수 있는지를 제한하며, Query Plan Cache Methods는 옵티마이저를 통해 최적화된 Query Plan을 모니터링할 수 있다.


보안성 강화

2.6부터 향상된 SSL 지원, X.509 기반 인증을 지원한다. 또한 컬렉션 레벨에서 사용자가 직접 권한을 설정하고 지정하는 기능과 더불어 admin 데이터베이스에 있는 모든 user와 사용자 정의 role 데이터를 저장ㆍ관리할 수 있는 Global User 기능을 지원한다.


Insert, Update 개선

Insert와 Update 속도가 개선됐으며, 수정된 데이터의 일관성 역시 개선됐다. 이를 위해 새로운 연산자들이 추가됐다.
MongoDB는 2.x release 이후로 꾸준한 release update가 이뤄지고 있다 (stable 버전으로 확정되기까지 기준으로 update 주기는 1년 내외). release가 거듭될수록 성능과 안정성이 향상돼 지금보다 앞으로를 더 기대하게 만들고 있다. 이러한 발전은 해외에서의 대규모 투자 유치와 금융권에서의 도입 사례로 이어지고 있다. 최근에는 관계형 데이터베이스와 병행해 쓰이는 사례도 늘어나고 있다.


MongoDB 아키텍처

MongoDB의 데이터 구조

MongoDB의 데이터 구조는 광범위하게 더블 링크드 리스트를 사용한다. 데이터의 각 컬렉션은 인접한 디스크 공간을 나타내는 익스텐트의 링크드 리스트로 이뤄져 있다. 각 익스텐트는 데이터 레코드의 또 다른 링크드 리스트의 head와 tail을 가리킨다. 각 데이터 레코드는 BSON 타입으로 부호화된 실제 데이터뿐만 아니라 도큐먼트로의 링크드 리스트를 포함하고 있다. MongoDB의 논리적 구조는 아래와 같이 구성된다.

Database > Collections > Extents > Data Records > Documents

[그림 Ⅱ-2-10] MongoDB 데이터 구조

[그림 Ⅱ-2-10] MongoDB 데이터 구조




[Note]

다음은 MongoDB를 접하는데 있어 반드시 알아야 할 개념이다
  • 데이터베이스(database): 컬렉션의 논리적/물리적 모음
  • 컬렉션(collection): 도큐먼트의 모음. 효과적인 구성을 위해 구조적으로 또는 개념적으로 유사한 도큐먼트를 모으는게 일반적이다(기존 RDBMS의 테이블(table)과 비슷한 의미)
  • 익스텐트(extent): 데이터가 저장되는 논리적인 단위
  • 도큐먼트(document): 정렬된 key/value의 집합(기존 RDBMS의 행(raw)과 비슷한 의미)

MongoDB 복제 구조

MongoDB의 High Availability는 복제 세트(Replica Set)를 통해 이뤄진다. 데이터 일관성을 위해 모든 변경(insert, update, delete)은 Primary DB로 요청되고, 이는 비동기적으로 다른 Secondary DB에 복제된다.

[그림 Ⅱ-2-11] MongoDB Replication Model

[그림 Ⅱ-2-11] MongoDB Replication Model

복제 세트에서 멤버들은 서로 연결돼 있고, heartbeat 메시지를 교환한다. heartbeat 메시지를 놓친, 즉 다운된 서버는 다른 멤버에 의해 발견돼 복제 세트에서 제거된다. 만약 그 제거된 서버가 되살아난다면, 이는 다운된 이후 놓친 최신 정보에 대한 업데이트를 위해 프라이머리와 연결됨으로써 다시 클러스터에 조인된다. 만약 긴 시간 다운된 이유로 다운 이후의 정보를 제대로 따라잡을 수 없을 때는 프라이머리로부터 전체 데이터를 모두 리로드해야 한다.


MongoDB 샤딩 구조

[그림 Ⅱ-2-12] MongoDB Sharding Model

[그림 Ⅱ-2-12] MongoDB Sharding Model

쓰기 요청(write request)의 부하 분산을 위해 MongoDB Shard를 사용할 수 있다. 샤딩 세팅시에 컬렉션은 파티션 키에 의해 청크로 분할되고, 이러한 청크는 여러 개의 샤드로 분산된다. 즉 각각의 샤드는 복제 세트가 되는 것이다. MongoDB 샤딩은 크기 제한이 없는 데이터 컬렉션을 효과적으로 제공할 수 있으며, 이는 빅데이터를 다루는 데 있어 매우 중요한 요소가 된다.

샤딩 모델에서는 반복을 위해, 단일 파티션 키는 샤딩 클러스터에 저장된 각각의 컬렉션에 명시돼 있다. 파티션 키의 키 스페이스는 chunk라는 인접한 키 범위로 나눠지고, 이는 상응하는 샤드들에 의해 처리된다.


ReplicaSet 설정

복제(Replication)는 동일한 데이터를 여러 서버에 저장하는 것을 의미한다. NoSQL의 안정성에 대해 의문이 제기될 때 항상 먼저 거론되는 솔루션이며, 분산 시스템을 논하는데 있어 오랫동안 주요 이슈로 자리잡고 있기도 하다. MongoDB를 다루는 관련 도서에서도 이미 언급됐겠지만, 간단하게나마 복제 세트(ReplicaSet) 설정을 살펴 보자.
복제를 위해 마스터-슬레이브 복제(Master-Slave Replication)와 복제 세트(Replica Set)를 사용할 수 있다. 우선 마스터-슬레이브 복제를 위해, 마스터 노드(Master Node)와 슬레이브 노드(Slave Node)를 설정하자. 슬레이브 노드는 시스템 구성에 따라 그 개수를 다르게 가져갈 수 있지만, 기본적으로 권장하는 사항은 1개의 마스터 노드와 3개의 슬레이브 노드다.
먼저 다음과 같이 마스터 모드로 mongod 인스턴스를 시작해보자.

> mongod --dbpath /mongodb/data/master/ --port 9000 --master

슬레이브 모드로 mongod 인스턴스를 시작하는 방법은 다음과 같다.


  1. 1번 슬레이브

    1번 슬레이브

  2. 2번 슬레이브

    2번 슬레이브

  3. 3번 슬레이브

    3번 슬레이브

각각의 dbpath와 포트번호를 다르게 지정해 줘야 하며, 슬레이브 노드 추가시 마스터 노드 로그에서 아래와 같은 내용을 확인할 수 있다.

Sun Dec 7 06:50:45.614 [initandlisten] connection accepted from 127.0.0.1:1488 #2 (2 connections now open)




[Note]

슬레이브의 복제 연결 정보를 보여주는 printSlaveReplicationInfo() 명령은 마스터 노드에서 실행 시 오류 메시지를 보여준다. 오류 메시지를 보여준다고 해서 시스템에 영향을 미치는 것은 아니다. 간혹 현재 자신이 접속한 노드를 확인하는 수단이기도 하니, (권장사항은 아니지만) 테스트로 해 보는 것도 나쁘지는 않다.


다음으로 복제 세트를 살펴 보자. 복제 세트는 다음과 같은 순서로 구성된다.

Primary, Secondary, Arbiter 설정 → 복제 세트 초기화 → 복제 세트 결과 확인


Primary, Secondary, Arbiter 설정
  1. ① Primary 설정

    > mongod --replSet testSet --dbpath /mongodb/data/primary/ --port 9000

  2. ② Secondary 설정

    > mongod --replSet testSet --dbpath /mongodb/data/secondary/ --port 9001

  3. ③ Arbiter 설정

    > mongod --replSet testSet --dbpath /mongodb/data/arbiter/ --port 9002

복제 세트 초기화
  1. ① db.runCommand를 이용한 방법

    쉘에 접속해 다음과 같이 실행한다.

    > db.runConmmand({"replSetInitiate" : {"_id" : "testSet", "members" : [ {"_id" : 0, "host" : "localhost : 9000"}, {"_id" : 1, "host" : "localhost: 9001"}, {"_id" : 2, "host" : "localhost: 9002", arbiterOnly: true}]}})

  2. ② rs.initiate를 이용한 방법

    쉘에 접속해 다음을 실행한다.

    > config = {"_id" : "testSet", "members" : [{"_id" : 0, "host" : "localhost : 9000"}, {"_id" : 1, "host" : "localhost:9001"}, {"_id" : 2, "host" : "localhost:9002"}]}



[Note]

위는 config에 members 배열로 프라이머리와 세컨더리를 설정해 준 것이다. 다만 이 경우에 9002 포트를 사용하는 세 번째 노드는 아비터가 아닌 세컨더리로 추가된 것임을 알려둔다. 아비터 노드는 별도의 옵션으로 추가해 주어야 한다. 이 경우에도 아래와 같이 두가지 방식이 쓰인다.

> rs.addArb("localhost:9002")
> rs.add({"_id" : 2, "host" : "localhost:9002", "arbiterOnly" : true})

config를 설정했으면 아래와 같이 rs.initiate를 실행해 준다.

> rs.initiate(config)


복제 세트 결과 확인

어느 방법을 사용하든 정상적으로 복제 세트가 설정되면 아래와 같은 결과화면을 확인할 수 있다.

> {"info" : "Config now saved locally. Should come online in about a minute.", "ok" : 1}




[Note]
  1. 1. 복제를 수행하는 두 가지 방식 중 복제 세트(Replica Set) 방식을 권장한다. 이는 장애 복구 과정의 유연함 때문인데, Master에 장애 발생 시 복제 세트는 자동 선출이 가능한데 비해 마스터-슬레이브 방식은 수동으로 마스터를 재구성하고 재동기화를 해야 하는 번거로움이 있기 때문이다. 실제로 MongoDB 측에서도 복제 세트를 권장하고 있다. 하지만 이 점 역시 예외가 존재하니 자세한 사항은 ‘6. Tips for MongoDB’에서 다시 서술하겠다.
  2. 2. 복제를 논할 때 oplog는 필수적인 존재다. 이는 도큐먼트에 대한 모든 변경사항을 기록하고 있으며, 장애 복구시에 중요한 역할을 담당하기 때문이다. 별도의 모니터링 스크립트를 구성하는데 있어서 참고하기에도 좋기 때문이기도 하다. 따라서 oplog는 가급적 많은 디스크 공간을 할당해 주는 것이 좋다. 이 점은 뒤에 나오는 6. Tips for MongoDB에서 다시 소개하겠다.

샤딩 설정

샤딩(Sharding)은 데이터를 분할해 여러 시스템에 저장하는 방식을 말한다. 복제(Replication)와 더불어 MongoDB의 장점을 표현하는 대표적인 특징 중 하나다. 샤딩으로 인해 스케일업이 아닌 스케일 아웃으로 많은 데이터를 처리할 수 있게 되었다. 앞서 ‘3. Replication 설정’과 마찬가지로 이번에도 간단한 설정 방법과 팁 위주로 소개하겠다.


샤딩 구성

샤딩은 다음과 같은 순서로 진행된다.

Config Server 설정 → mongos 프로세스 활성화 → 데이터베이스 샤딩 활성화 → 컬렉션의 샤딩 활성화


Config Server 설정
  1. ① 3대의 서버에서 Config Server를 설정할 경우

    server -1 > mongod --configsvr --dbpath /var/lib/mongdb -f /var/lib/config/mongod.conf / server -2 > mongod --configsvr --dbpath /var/lib/mongodb -f /var/lib/config/mongod.conf / server -3 > mongod --configsvr --dbpath /var/lib/mongodb -f /var/lib/config/mongod.conf

  2. ② 1대의 서버에서 Config Server를 설정할 경우

    > mongod --configsvr --dbpath /mongodb/cfg1 -f/var/lib/config/mongod.conf --port 20001 > mongod --configsvr --dbpath /mongodb/cfg2 -f /var/lib/config/mongod.conf --port 20002 > mongod --configsvr --dbpath /mongodb/cfg3 -f /var/lib/config/mongod.conf --port 20003



[Note]

기본적으로 config server는 3대를 권장한다. 혹시 모를 config server의 장애에 대비하기 위함이며, mongos는 이러한 config server에 동일한 데이터가 있도록 하기 위해 2단계 커밋(two-phase commit)을 수행한다. 많은 수의 config server는 이러한 2단계 커밋이 생각보다 많은 시간을 소요할 수 있기 때문에 문제가 될 수 있다. 환경에 따라 다를 수 있지만 기본적으로는 3개로 구성하는 것이 적합하다.



mongos 프로세스 활성화
  1. ① 3대의 서버에서 Config Server를 설정할 경우

    > mongos --configdb server-1:27019, server-2:27019, server-3: 27019

  2. ② 1대의 서버에서 Config Server를 설정할 경우

    > mongos --configdb server-1: 2001, server-1: 2002, server-3: 20003

데이터베이스 샤딩 활성화

우선 mongo 쉘에서 mongos 인스턴스에 연결한다.

=> mongo --host mongos가 실행중인 호스트 이름 --port mongos가 사용하는 port

> mongo --host server-1 --port 27019

그리고 각 샤드 서버를 등록한다. 이 경우 두 가지 방법을 적용할 수 있다.


  1. ① sh.addShard 이용
    => sh.addShard("복제세트 이름/호스트 이름:포트, ....")

    > sh.addShard("testSet/server-1:27017, server-2: 27017, server-3:27017"){"added" : "testSet / server-1:27017, server-2:27017, server-3:27017", "ok" : true}

  2. ② db.runCommand 이용

    >db.runCommand({addshard : "server-1: 27017"}) >db.runCommand({addshard : "server-2: 27017"}) >db.runCommand({addshard : "server-3:27017"})

    그리고 다음을 수행해 데이터베이스 샤딩을 활성화한다.

    > sh.enableSharding("shardtest")

    또는

    > db.runCommand( { enableSharding : "shardtest")

샤드 키 설정 방식

효과적인 샤딩인지 아닌지를 평가하는 분기점 중 하나는 샤드키 설정이다. 일반적으로 샤딩 구성을 위해 일반적으로 오름차순, 무작위, 위치기반 방식의 샤드키를 구성한다. MongoDB 2.4 releases부터 해시 샤드키 방식이 도입됐으며, 현재까지는 가장 합리적인 방식으로 보인다. 다만 해시 샤드키는 범위 쿼리(range query)를 할 수 없는 단점이 있어서 모든 방식에 적합한 방식이라고 볼 수는 없다. 따라서 특정 방식 고수보다는 현재 개발 또는 운영 중인 시스템의 데이터 특성을 파악하고 그에 맞게 샤드키를 설정하는 것이 합리적이라 할 수 있다. 적절한 샤드키 선택을 위한 가이드는 MongoDB 매뉴얼 중 "Considerations for Selecting Shard Keys13)부분을 참조하기 바란다.


샤딩 관리의 문제와 점보 청크 다루기

샤딩을 수행하는데 있어 데이터를 여러 논리적 단위로 분할하게 된다. 이를 청크(Chunk)라 부르며, 기본 크기는 64MB이다. 만약 청크가 커져서 정해진 크기(64MB)를 넘어가게 되면 이 청크를 분할하게 된다. 하지만 가끔씩 샤드키 설정의 문제로 특정 청크의 데이터가 최대 청크 크기보다 커지면, 밸런서는 청크의 이동을 허용하지 않게 된다. 결국 이렇게 나누어지지도 옮겨지지도 않는 청크는 운영상의 이슈거리로 작용하는데, 이를 점보 청크(jumbo chunk)라 부른다. 이러한 점보 청크가 존재하기 시작하면, 점보 청크가 속한 샤드의 크기가 다른 샤드에 비해 훨씬 빨리 커지는 문제가 발생한다. 이는 균등한 샤딩 유지를 방해하는 요소가 된다.
그럼 점보 청크의 방지는 어떻게 할 수 있을까? 대부분의 경우는 고르지 못한 샤드키 문제로 발생된다. 초기 설정시에는 균등한 값을 보장할 수 있는 경우더라도 특정 일자에 예상할 수 없었던 오류가 발생하거나 새로운 패턴의 데이터가 발생한다면 샤드키 조정이 필요해질 것이다. 초기에는 데이터양이 많지도 않고 대부분의 데이터가 일정 규칙을 가지고 있기 때문에 샤드키 구분을 약하게 설정하게 된다(어쩌면 앞으로도 이렇게 약한 기준의 데이터가 발생하길 바라는 것일지도 모른다).
따라서 향후 발생할 데이터의 성향을 정확히 파악하고 가능한 세밀한 기준으로 샤드키를 생성해 분할할 수 있도록 하자. 물론 데이터 발생 추이를 수시로 살펴보면서 샤드키를 조정할 수 있지만, 데이터양이 증가할수록 이러한 점보 청크에 대한 대처가 어려워지므로 향후 운영의 편의성을 위해 조금 더 세심하게 살펴볼 필요가 있다. 그리고 이러한 점보 청크가 발생했을 때의 대처는 ‘6. Tips for MongoDB’에서 소개하겠다.


MongoDB 활용사례

MongoDB는 초기에는 해외를 중심으로 확산됐고, 국내에서도 2011~12년을 기점으로 활용되기 시작했다. SNS 멘션, 잡오퍼, 논의 등으로 데이터베이스 순위를 측정하는 DB-Engines(http://db-engines.com/en/ranking)에서도 MongoDB는 ‘DBMS of the year 2013’을 기록하며 (http://db-engines.com/en/blog_post/25), 2014년 12월 현재 5위를 달리고 있다.


해외 사례

SourceForge는 MongoDB의 대표적인 적용 사례다. SourceForge는 "Project Summary", "File Browser", "Download" 등과 같은 페이지에 대부분의 부하가 집중되었는데, 이를 해결하기 위한 백엔드 저장소로서 MongoDB를 적용했다.

[그림 Ⅱ-2-13] SourceForge 의 MongoDB 적용 사례

[그림 Ⅱ-2-13] SourceForge 의 MongoDB 적용 사례

SourceForge가 백엔드 저장소로서 고려한 요소는 성능, 확장성, 복잡한 데이터 쿼리 처리 및 개발적용의 용이함 등이었으며 이러한 요소를 모두 만족한 것이 MongoDB였다. 그리고 해당 시스템에서는 잦은 읽기(read)와 Develop으로부터 변경(update)이 간헐적으로 발생하였으며 MongoDB의 도입은 이러한 처리의 성능을 높이게 해주는 역할을 하였다. 또한 MongoDB는 데이터 복제와 백업을 편하게 해줬고, 스키마의 자유로운 특성 때문에 상대적으로 잦은 마이그레이션을 방지해줬다. 확장성 또한 향상되었으며, 무엇보다 비약적인 성능의 향상을 경험할 수 있었다. (서버별로 초당75개 이상의 동적 페이지 요청 처리)
일반적으로 모든 종류의 데이터나 상황에 적합한 데이터베이스는 존재하지 않는다. 운영환경, 데이터의 종류, 각 요소별 우선순위 등을 고려하여 데이터베이스를 선정하게 되는데 SourceForge의 요구조건이 MongoDB가 내세우는 장점과 잘 맞아떨어진 사례로 생각할 수 있다. 다만 SourceForge의 MongoDB 도입 사례에서 얻을 수 있는 교훈은 스스로가 관리하는 시스템에 대한 충분한 이해가 바탕이 되었다는 점이다. 물론 각 데이터베이스들이 내세우는 장점은 활용할 수만 있다면 모두 좋은 기능들이다. 다만 서비스나 데이터의 종류에 따라 그러한 기능의 효과가 반감되는 경우도 종종 발생한다.
하지만 SourceForge의 경우 단순히 NoSQL 도입을 통한 비용절감뿐만 아니라, 본인들의 시스템에 정말 필요한 튜닝 포인트를 토대로 데이터베이스 후보군을 검토하였고, 나름의 충분한 노력을 통해 현재의 변화를 이끌어 냈다는 점에서 좋은 레퍼런스가 될만한 사례이다.


국내 사례

LG유플러스가 상용 그룹웨어 서비스에 MongoDB를 적용했다. 그룹웨어 서비스는 100명 이하의 중소기업을 대상 고객으로 메일, 메신저, 전자결재, 게시판 등의 기능을 제공한다.

[그림 Ⅱ-2-14] LG유플러스 그룹웨어 서비스 웹 페이지

[그림 Ⅱ-2-14] LG유플러스 그룹웨어 서비스 웹 페이지

[그림 Ⅱ-2-15] LG 유플러스 그룹웨어 서비스

[그림 Ⅱ-2-15] LG 유플러스 그룹웨어 서비스

그룹웨어 서비스에서 생성되는 데이터 중 알림로그, 쪽지, 그리고 인증로그를 처리하기 위해 MongoDB를 도입하였으며, 이는 발생 빈도가 높고 큰 저장 용량을 필요로 한다는 공통점이 있다.

[그림 Ⅱ-2-16] LG 유플러스 그룹웨어 서비스의 MongoDB 구성 아키텍처

[그림 Ⅱ-2-16] LG 유플러스 그룹웨어 서비스의 MongoDB 구성 아키텍처

LG 유플러스 그룹웨어 서비스의 MongoDB 아키텍처는 별도의 샤딩구성이 없는 심플한 3노드 복제세트 구성으로 이뤄졌지만, 초기에 구성한 서비스 부하를 처리하는데는 문제가 없다. 확장이 용이하기에 서비스 증가량 정도에 따라 충분히 추가 구성이 가능한 형태다.
LG 유플러스의 MongoDB 도입은 그룹 내에서 상용 시스템을 위한 저장소로 NoSQL을 선제적으로 적용한 사례로서 의의가 있으며, 향후 타 서비스에 MongoDB 도입시 기술적인 레퍼런스를 확보할 수 있다는 점에서 긍정적인 효과를 찾을 수 있다. 또한 기존의 상용 데이터베이스에서 벗어나 적극적으로 오픈소스DB를 도입함으로써 전체적인 비용 절감 효과는 물론, 기존 제품에 비해 용량 증설에 따른 확장이 용이하여 운영의 부담을 덜 수 있게 되었다.
물론 기존에 사용하지 않던 NoSQL중에 하나를 도입하였기 때문에 MongoDB 전담 인력을 양성하는 것과 충분한 기술적 레퍼런스를 확보하는 것은 유플러스가 앞으로 안고가야 할 숙제이다. 하지만 이는 새로운 기술을 도입하는 모든 회사가 안고 있는 공통적인 문제점이기에 시간이 흘러 운영 노하우가 쌓이면 자연스레 해결될 문제일 것이라 생각한다. 현재 유플러스의 MongoDB 아키텍처는 기본적인 형태의 구성이지만, 기존 서비스의 용량 증설시 서버 대수를 점차 증가시켜 여기에 추가 레플리카셋 구성과 샤딩을 적용하는 등의 확장 계획을 가지고 있다. 또한 서비스의 범위가 변경될 경우 도메인, 트랜잭션 유형에 따른 아키텍처 변경도 수행할 예정이다.


Tips for MongoDB

앞서 필요한 팁들을 간단히 서술했지만, 이번에는 자주 발생하는 또는 자주 거론되는 MongoDB 관련 팁들을 살펴보자.


32비트 or 64비트

될 수 있으면 64비트 환경에서 MongoDB를 사용하도록 하자. 32비트 시스템에서 MongoDB가 사용 가능한 데이터의 크기는 2.5GB뿐이다.


충분한 메모리는 미덕이다.

최근의 NoSQL들은 모두 충분한 메모리를 전제로 한다. MongoDB 역시 마찬가지다. 인덱스 크기가 메모리 크기보다 커지면 MongoDB는 속도에 큰 지장을 받게 되므로, 충분한 메모리 확보는 필수이다. 하드웨어 중 투자 우선순위를 선정한다면 첫째가 바로 메모리다.


복제 세트는 필수, 샤딩은 옵션

HA를 위해서라도 복제 세트는 필수다. 하지만 샤딩이 필수인지는 생각해 볼 문제다. 샤드 키 설정에 따라 성능에 영향을 미칠 수도 있기 때문에 샤딩이 필요한 시스템인지, 샤딩을 한다면 샤드 키를 어떻게 설정할 것인지를 충분히 검토해 보기 바란다. 적어도 다음과 같은 경우라면 샤딩을 권고할 수 있다.


  • 쓰기 부하가 너무 크다.
  • 전체 데이터를 RAM이 수용하기 힘들다.
언제 인덱스를 사용할 것인가?

인덱스(Index)는 한정된 메모리에서 많은 작업을 수행하기 위해 반드시 필요한 요소다. 하지만 인덱스를 통해 검출된 데이터가 컬렉션의 절반 이상이 된다면 그 시점부터는 오히려 부하를 발생시키는 요소가 될 것이다. 여러 쿼리를 수행해야 한다면, 복합 인덱스(Compound Indexes)를 사용하자.


점보 청크 분산시키기

앞서 샤딩을 다루면서 점보 청크의 문제점을 소개했다. 이러한 점보 청크가 발생했을 때는 다음과 같은 절차로 처리한다.

밸런서를 종료한다. → 임시로 청크의 크기를 늘린다 (최대 청크 크기보다 큰 청크를 옮기지 못하기 때문에 수행하는 절차다). → 점보 청크를 기존 샤드에서 다른 샤드로 이동한다. → 다른 샤드들과 비슷한 개수의 청크를 가질 때까지 남아있는 청크에 splitChunk를 실행한다. → 청크 크기를 원래 값으로 설정하고 밸런서를 켠다.


Oplog는 가급적 많은 용량을 할당하자.

앞서 언급한 것처럼 Oplog는 모든 변경사항을 기록하고 있다. 이러한 변경 사항에 대한 기록은 장애 발생시 복구를 위한 측면에서 매우 중요한 역할을 담당한다. 즉 Oplog에 담겨 있는 정보의 양만큼 특정 상황에 대한 모니터링과 장애 복구가 가능하다는 의미다.
MongoDB 공식 메뉴얼14)에서 언급한 Oplog 기본(default) 크기는 다음과 같다.


  • 64비트 Linux, Solaris, FreeBSD, Windows 시스템에서 이용 가능한 여유 디스크 공간의 5%를 할당한다. 이는 최소 1GB보다는 크고, 최대 50GB를 넘지는 못한다.
  • 64bit OS X 시스템에서 183MB의 공간을 할당한다.
  • 32bit 시스템에서 48MB의 공간을 할당한다.

이러한 Oplog 크기는 변경 가능하며, 다음과 같은 경우에는 좀 더 큰 크기의 Oplog를 갖도록 권장한다.


  • 한 번에 여러 개의 도큐먼트에 업데이트를 수행하는 경우
  • 데이터를 insert하는 만큼 delete를 수행하는 경우
  • 작업 부하의 상당 부분이 업데이트인 경우
Config server는 가급적 한 대의 서버에서만 구성하지 않도록 한다.

Config server 자체가 부하가 크지는 않지만, 3개의 config server를 한 대의 장비에 구성하지 않도록 하자. 너무 당연한 얘기지만 한 대의 물리 서버에 집중했을 경우의 문제(물리 서버 장애)와 더불어 클러스터 상태에 영향을 미칠수도 있기 때문이다.
지금까지 다룬 내용은 NoSQL 적용을 위한 가이드이기 때문에, 전반적인 NoSQL 이론과 HBase, MongoDB를 분석했지만 아쉽게도 지면상 담지 못한 내용도 많다. 예를 들어 HBase 운영 기술, 타범주에 속하는 NoSQL 소개, NoSQL 데이터 모델링 기술 등이다. NoSQL에 데이터 모델이 없다고 생각하면, 정상적으로 NoSQL을 활용할 수 없다. 다만 RDB와 달리 NoSQL은 제품마다 모델링 방식이 다르기 때문에 이번장에서 다루기는 어려웠다. HBase와 MongoDB를 시작으로 다른 NoSQL도 직접 사용해 그 용도와 차이를 몸소 체험하고 이해한다면 이번 장의 목표은 달성한 셈이다.