2014년 8월 17일 일요일

Couchbase 사용기 - 2

Couchbase 는 기본적으로 key/value 타입의 NoSQL 제품입니다. 다시 말해 Unique 한 Key 을 이용해서 문서(Document)을 빠르게 가져오는 것을 목적으로 한 제품이라는 것이죠. 그렇다면 사용자가 읽어오길 원하는 문서의 Key 을 알고 있다면 언제든지 문서를 가져올 수 있습니다. 실제로 Couchbase SDK 에서는 set() 와 get() 을 통해서 문서를 저장하고 가져올 수 있으며 매우 단순한 형태이기 때문에 초급 개발자도 금새 배울 수 있습니다.
하지만, 현실상 특정 key 의 문서만 가져오는 경우는 매우 드뭅니다. 가까운 예로 게시판의 경우만 해도 특정 조건을 만족하는 데이터(=문서)의 집합을 가져와서 화면에 출력하는 형태가 되죠. Couchbase 는 이러한 것을 map/reduce 기반의 View 을 통해 처리할 수 있도록 하고 있습니다. map/reduce 는 Couchbase 의 독자적인 기술은 아닙니다만, 솔루션마다 처리 방식을 조금씩 다르게 하기 때문에 여기서 Couchbase 의 View 을 통해 Couchbase 만의 특징을 살펴보고자 합니다.

Couchbase 의 웹콘솔에 접속하면 Views 항목을 볼 수 있습니다. 여기서 View 을 생성할 수 있습니다. 이 때 "Design Document Name" 이라는 항목과 "View Name" 을 입력하게 되어 있는데, 쉽게 말해 Design Document Name 은 뷰의 그룹명, View Name 은 View 의 이름입니다. 생성을 하게 되면 "VIEW CODE" 부분에 "Map" 와 "Reduce" 을 입력할 수 있습니다. 모두 Javascript 문법을 이용한 하나의 function 이며, 매개변수는 doc 와 meta 을, View 에서 생성되는 인덱스에 들어가는 정보는 emit() 으로 입력할 수 있습니다.

자세한 사용법을 설명하는 것은 아니므로 구현 부분에 대해서는 다른 기회가 되면 글을 적어보겠습니다. 여기서 설명할 내용은 View 에 대한 특성입니다.

먼저, View 은 처음 생성할 때를 제외하고는 문서를 추가하거나 갱신, 삭제 등의 작업을 할 때 처리된 후 저장됩니다. 처음 생성할 때에는 모든 문서를 읽어서 View 의 인덱스를 구축합니다. 이 구축 작업은 비동기로 이루어지기 때문에 생성 버튼을 눌러도 바로 적용되지 않고 수초~수십초의 시간이 흐른 뒤에야 제대로 View 의 결과가 나타납니다. 그리고 문서가 추가되는 등의 작업이 발생하면 메모리에 저장을 하면서 map/reduce 코드를 통해 인덱스 정보가 생성된 뒤에 2.5.1 까지는 디스크에, 3.0 부터는 메모리에 인덱스 정보가 저장되기 때문에 저장 완료 후 즉시 조회를 하면 바로 적용이 되질 않아서 추가/갱신/삭제된 문서 정보가 제대로 반영이 안되어 있습니다. 2.5.1 이하 버젼에서는 3초 정도 걸리는 경우가 비일비재합니다. 3.0 부터는 메모리에 저장되기 때문에 1초 이하가 될꺼라고 합니다만 완벽히 동기화 된다고 볼 수 없습니다.

그리고 View 는 조건에 대한 처리가 아니라 문서에 대한 처리이기 때문에 사용자가 조회시 보내는 매개변수에 따라 그때그때 조회하는 것이 아니라 인덱스된 문서의 key의 범위 사이의 값을 돌려보내주는 것입니다. 그래서 배열 형태로 2개 이상의 매개변수를 보낼 경우 일반적인 RDBMS 의 검색과는 판이한 검색결과가 나옵니다. 게다가 숫자형의 값일 경우 일반적인 조회에서는 숫자형태로 저장되어 있지만, View 을 생성하면 숫자형이 문자형으로 바뀌기 때문에 인덱스 생성시 1,2,3,...,10,11,12,...,20,21,22,... 와 같은 형태가 아닌 1,10,100,...,11,... 와 같이 정렬이 됩니다. 복합적인 매개변수의 경우도 아래와 같이 됩니다.


  1. [1,"daum","korea"]
  2. [1,"naver","korea"]
  3. [3,"bing","usa"]
  4. [3,"google","usa"]


위와 같이 인덱스가 생성되면(입력 순서에 상관없이 세 값의 순서대로 정렬되어서 인덱스가 생성됩니다) 아래와 같은 형태로 범위검색을 할 수 있습니다.

startKey : [1,"dreamwiz", "korea"]
endKey : [3,"facebook", "usa"]

처음에는 RDBMS 의 or 조건처럼 검색이 되나 싶었습니다만(엔투엠에서도 그런 식으로 이해하시면 될 것 같다고 했습니다만...) 사실 그렇지 않더군요. 어찌보면 더 간단한 것인데요...
추가로 startKey 와 endKey 을 입력한다고 하면 어떤 위치에 값이 들어가게 될까요? 각각 1 번 다음과 3번 다음에 위치하게 될 것입니다. 그러므로 위의 범위 검색 결과는 2 번과 3 번이 되게 됩니다. 이런 식으로 범위 검색을 통해서 이미 입력된 인덱스의 값을 가져오기 때문에 RDBMS 의 like 검색과 같은 것은 이용할 수도 없고, 숫자를 이용한 범위 검색도 불가능합니다. 그래서 항상 View 을 생성하고 난 뒤 원하는 결과가 나오는지 꼼꼼히 확인하는 것이 중요합니다.

좀 더 다양한 검색 결과를 원하신다면 검색엔진과의 연동이 필요합니다. Couchbase 에서는 Elasticsearch 와 연동하는 방법을 제공하고 있습니다. 이 방법을 통해 View 의 map/reduce 방식이 가지는 한계를 어느 정도 극복할 수 있습니다.

또다른 사용기는 다음 글에...

댓글 6개:

  1. Design Document 에 view 를 넣는 룰에 대해서 들을 수 있을까요? 어느 경우에 기존 Design Document가 아닌 새로운 Design Document를 만들어서 view를 배치하시나요?

    답글삭제
    답글
    1. 카페에도 글 올리셨더군요. ^^;;;

      Bucket 도 여러개 만들어서 내용에 따라 구분해서 저장 가능하듯이(물론 패스워드 등을 통해 접근제어도 되기 때문에 좀 다르긴 하지만) view 들을 관리할 때 그 내용에 따라 분류하기 위해 쓰는게 아닌가 싶습니다. 내부적으로야 구별을 해두는 이유가 있을지 모릅니다만, 실제 사용할 때 Design Document 가 나뉜다고 할지라도 사용상에는 차이가 없더군요.

      삭제
    2. 답변 고맙습니다! 게임쪽 일하신다고 들었는데요. 혹시 transaction 관련 처리는 어떻게 하셨는지요? 2PC 구현하여 쓰셨나요? 주변에 쓰는 분들이 없어서 고민이 많습니다 ^^;

      삭제
    3. 많은 글에도 나와있지만, NoSQL 제품들은 보통 transaction 을 지원하지 않습니다. 이번 MeetUp 행사 때에도 제가 질의를 했었는데...Lock 을 이용해서 억지로 비슷하게 동작은 하게 합니다만...이게 완벽한 것도 아니고...아예 구조를 생각할 때 문제가 없는 형태로 가는게 맞는 것 같습니다. 다행이라고 해야할지 모르겠지만, 장애를 한 번도 겪어보질 않아서요...Two-Phase Commits 까지는 고려를 안했고, 게임 특성상 로그를 꽤나 남기기 때문에 문제가 생기면 로그를 바탕으로 수작업으로 데이터 보정을 하고 있습니다만...한 번도 이런 일은 발생하진 않았습니다.

      삭제
    4. 경험담 나눠주셔서 고맙습니다. 답변해주신 걸로 많이 배웠습니다 :)

      삭제