기술자료

DBMS, DB 구축 절차, 빅데이터 기술 칼럼, 사례연구 및 세미나 자료를 소개합니다.

성공적인 NoSQL 도입을 위한 키포인트 : NoSQL 데이터 모델링

기술자료
DBMS별 분류
Etc
작성자
dataonair
작성일
2013-12-23 00:00
조회
7233



성공적인 NoSQL 도입을 위한 키포인트

NoSQL 데이터 모델링



NoSQL은 RDBMS와 비교해 데이터 저장구조나 그 특성이 매우 다르다. NoSQL을 이용한 구현은 데이터 모델링을 얼마나 잘했느냐가 개발 성공 여부의 90% 이상을 차지한다. RDBMS 기반의 모델링이 개체 기반으로 시작해 정규화를 통해 테이블을 설계하고 쿼리를 이용해 결과를 뽑아냈다면, NoSQL은 역순으로 쿼리 결과를 먼저 설계한 후에 중복을 허용해 데이터를 저장하는 테이블 구조를 정의한다. 이 글에서는 NoSQL 기반의 데이터 설계를 하는 데 사용할 수 있는 주요 패턴을 소개한다. 조대협 bwcho75@gmail.com|자바스터디 초대 운영자, 한국자바개발자협의회(JCO)의 1대 부회장을 거쳤고 현재 개인 블로그(http://bcho.tisoty.com)와 페이스북 서버사이드 아키텍트 그룹(http://www.facebook.com/groups/ serverside)을 운영하고 있다. BEA, NHN, 오라클 등에서 웹로직 등 미들웨어 기반 미션 크리티컬 시스템과 대규모 글로벌 분산 시스템 기술 지원, 개발 컨설팅 등을 수행했으며 마이크로소프트에서 클라우드 아키텍트로 활동했다. 현재는 국내 기업에서 아키텍트로 근무 중이며 글로벌 서비스를 커버하는 대규모 분산 시스템에 대한 설계 구현 업무를 맡고 있다.



NoSQL 데이터 모델링이란 NoSQL에 저장할 데이터들의 구조, 즉 테이블 설계를 하는 것을 말한다.

NoSQL 디자인 패턴


NoSQL도 DBMS이기는 하지만 우리가 지금까지 익숙하게 사용해왔던 RDBMS와는 그 특성이 매우 다르기 때문에 접근 방법을 바꿔야 한다.



NoSQL과 RDBMS의 데이터 모델링 차이


NoSQL을 사용해서 데이터 모델링을 하려면 근본적인 사상 두 가지를 바꿔야 한다.

● 개체 모델 지향에서 쿼리 결과 지향 모델링 RDBMS의 모델링 기법은 저장하고자 하는 도메인 모델을 먼저 분석한 후에 개체 간의 관계(relationship)를 식별하고, 테이블을 추출해내고, 테이블을 이용해 쿼리를 구현함으로써 결과를 뽑아내는 방식이다. NoSQL의 경우에는 이 접근 방법을 역순으로 진행해야 한다. RDBMS가 도메인 모델 -> [테이블 -> 쿼리] 순서로 진행을 했다면, NoSQL은 도메인 모델 -> [쿼리 결과 -> 테이블] 순서로 테이블을 디자인해야 한다. RDBMS의 경우 여러 가지 최적화된 기능으로 테이블을 가지고 자유롭게 쿼리 결과를 뽑아낼 수 있지만, NoSQL의 경우 복잡한 쿼리 기능이 없기 때문에 반대로 도메인 모델에서 어떤 쿼리 결과가 필요한지를 정의한 후에 이 쿼리 결과를 얻기 위한 데이터 저장 모델을 역순으로 디자인해야 한다.

● 정규화(Normalization)에서 비정규화(Denormalization) RDBMS 모델링에서는 데이터의 일관성과 도메인 모델과의 일치성을 위해 데이터 모델을 정규화한다. 그 중에서도 같은 데이터가 2개 이상의 테이블에 중복되게 저장하는 것을 제거하는데, NoSQL은 반대의 접근 방법이 필요하다. 쿼리의 효율성을 위해 데이터를 정규화하지 않고, 의도적으로 중복된 데이터를 저장하는 등의 비정규화된 데이터 모델 설계 방식으로 접근해야 한다.



NoSQL 데이터 모델링 절차


그러면 RDBMS와 NoSQL의 두 가지 결정적인 차이를 인식하고 NoSQL 기반의 데이터 모델링 절차를 살펴보자.



1) 도메인 모델 파악

먼저 저장하고자 하는 도메인을 파악한다. 어떤 데이터 개체가 있는지 개체 간의 관계는 어떻게 되는지 등을 분석하고 ERD를 그려서 도식화한다. RDBMS의 모델링 접근 방법이고, NoSQL에서는 이렇게 하지 않고 바로 애플리케이션 관점으로 접근하는 경우도 많은데, 필자의 경우는 도메인의 데이터 모델 분석이 없이 바로 애플리케이션부터 접근하는 이런 방식은 바람직하지 않다고 생각한다. NoSQL도 데이터베이스이고 저장할 데이터에 대한 명확한 이해 없이는 제대로 된 데이터 모델이 나올 수 없다. <그림 1>은 간단한 블로그 시스템의 데이터 도메인 모델이다. 이 블로그 시스템은 사용자 ID 기반으로 블로그의 분류(Category)를 가지고 있고, 분류별로 글을 작성할 수 있으며, 글에 파일을 첨부할 수 있고, 댓글을 달 수 있는 블로그이다. 이 예제에서는 검색이나 페이징 기능은 제외한다(단순화하기 위해).



column_img_748.jpg

2) 쿼리 결과 디자인(데이터 출력 형태 디자인)

column_img_749.jpg

다음으로 가장 중요한 과정인 쿼리 결과 디자인이다. ‘도메인 모델’을 기반으로 애플리케이션에 의해 쿼리되는 결과값을 먼저 정해야 한다. 앞서 예를 든 블로깅 시스템을 다시 살펴보자.
① 화면 좌측 상단에는 블로그 사용자의 포스팅 분류명들을 목록 형태로 출력한다.
② 포스팅 출력 화면에서는 상단에 포스팅의 분류명과 제목을 출력하고 이어서 포스팅 날짜, 본문 내용을 출력한다.
③ 다음에는 첨부파일들을 출력하는데 첨부파일 업로드 날짜와 파일명을 차례대로 출력하고 파일에 대한 링크를 출력한다.
④ 마지막으로 댓글을 출력하는데 댓글에는 작성일과 작성자 이름, 댓글 내용을 출력하고 작성자 이름에는 이메일을 링크한다.
이 출력 형식을 기반으로 어떤 쿼리가 필요한지를 정의해 보자.

① 전체 분류 출력
select categoryID,name from Category where userID=“사용자ID”
② 포스팅의 분류, 제목, 날짜, 본문 출력
select po.postID,po.Contents,po.date,ca.name
from Category ca,Post po
where userID=“사용자ID”
order by date desc
③ 첨부 파일 출력
select fileID,filename,date,fileLocation
from Attachment
where userID=“사용자ID” and postID=“현재 포스팅 ID”
order by date desc
④ 댓글 출력
select userID,email,Contents,date
from Comment
where userID=“사용자ID” and postID=“현재 포스팅 ID”
order by date desc

대략적으로 4개의 쿼리가 필요하다(물론 RDBMS 실제 구현에서는 좀더 최적화할 수 있겠지만 이해를 돕기 위해 단순 구현했다). 그러면 어떤 형태의 데이터 테이블들이 출력되는가
column_img_750.jpg

<그림 3>과 같이 애플리케이션의 출력 형태에 따른 데이터를 정리할 수 있다. 사실 이것이 가장 중요하다. 앞에서도 설명했듯이 NoSQL의 데이터 모델링은 도메인 모델을 중심으로 하는 것이 아니라, 이 애플리케이션의 데이터 출력 내용을 기반으로 하기 때문이다.

3) 패턴을 이용한 데이터 모델링

애플리케이션 데이터 출력 내용을 디자인했으면, 이제 이 내용을 가지고 NoSQL에 정의될 데이터 모델링을 시작한다. NoSQL은 Sorting, Grouping, Join 등의 RDBMS 기능을 제공하지 않기 때문에 이를 배제하고 Put/Get으로만 데이터를 가지고 올 수 있는 형태로, 데이터 모델 즉 NoSQL 내의 테이블을 재 정의해야 한다(이 데이터 모델링을 돕기 위해 이어지는 내용에서 NoSQL 데이터 모델링 기법을 다시 설명한다).
이때 가장 중요한 것은 Denormalization(중복허용)이다. 데이터를 가급적 중복으로 저장해 한번에 데이터를 읽어오는 횟수를 줄이도록 한다(이 IO 자체가 엄청난 부담이기 때문이다). 앞의 블로깅 시스템 데이터를 일반적인 NoSQL 스타일로 바꾸면 <그림 4>와 같다.
column_img_751.jpg

특히 Key 부분을 주목할 필요가 있는데, Join을 없애기 위해 아예 userID나 postID를 “:”로 구분되는 deliminator로 해서 Key에 포함시켰다. 이는 Join을 없애는 것 이외에 다른 이유가 또 있어서다. Ordered Key/Value Store의 경우에는 Key를 기반으로 소팅하기 때문에 첫 번째, 포스트 ID를 Sequential하게 증가시켜 나가면 같은 사용자의 글의 경우에는 Sorting이 가능하다. 두 번째, Grouping인데 포스팅을 출력하는 사용자에 대해 posting을 계속 출력해 나가면 순차적으로 Post가 출력되다가 해당 사용자의 포스팅 데이터가 끝나면 Key의 맨앞 userID가 다른 사용자 id로 바뀌기 때문에 where 문장이 없이도 특정 사용자의 포스팅을 순차적으로 출력할 수 있다. 뒤에서 설명할 데이터 모델링 패턴에서 Enumerable Key와 Composite Key Index 패턴을 참고하길 바란다.

4) 최적화에 필요한 기능들을 리스팅

이제 조금은 ‘NoSQL스럽게’ 디자인됐다. 이제는 NoSQL의 특성을 반영해 좀더 디테일한 디자인을 할 수 있다. 애플리케이션 및 데이터의 특성을 다시 한 번 들여다보자.
첨부(Attachment) 파일의 경우에는 포스팅이 작성됐을 때 레코드가 추가되며, 변경이 거의 없다. 그리고 첨부파일의 수는 그리 많지 않기 때문에, 하나의 필드에 모두 몰아서 저장할 수 있다. 이렇게 하나의 필드에 여러 개의 데이터를 저장할 경우에는 Document Store를 고려해 볼 수 있다.
그리고 앞에서도 언급했지만 블로그 포스트, 첨부파일, 댓글은 소팅돼야 하기 때문에 Ordered Key 형태가 필요하다. 현재 데이터 모델은 포스팅이 포스트된 순서대로만 출력하는 형태인데 분류 개념이 있기 때문에 분류에 따라서 포스팅을 출력하려면 분류 필드가 별도 필드로 들어가야 하고, 이 필드에 따라 where문으로 select할 수 있는 기능이 있어야 한다. 마치 RDBMS의 Index 같은 개념이 필요한데, 이러한 기능을 NoSQL에서는 Secondary Index라고 한다.
column_img_752.jpg

이런 옵션 등을 적용해보면 Posting 테이블은 <그림 5>와 같이 변경해볼 수 있다. 여기서 고려해야 할 점 하나를 짚고 넘어가자. NoSQL 제품들은 KV, Ordered KV, Document Store 등 데이터 모델로는 세 가지로 분류되긴 하지만, 세세한 내부 아키텍처나 기능들은 매우 다르다. 따라서 아주 자세히는 아니더라도, 어떤 NoSQL 제품군이 있고 대략적인 기능이 어떻게 되는지를 알아야 이 정도 수준의 설계를 할 수 있다. 물론 이 단계까지 설계되더라도 아직까지 완벽하지는 않다.
솔루션들이 스펙상에 “OOO 기능이 있다”고 하더라도, 그 기능이 제대로 돌아가는 건 아니다. Secondary Index의 경우 Cassandra나 Riak에서 지원은 하지만 실제 부하를 줘 가면서 테스트해 보면 성능이 잘 나오지 않는 경우가 많고, 내부 구조상으로도 고성능을 내기가 어렵다. 그래서 이런 부가 기능들은 직접 내부 구조를 분석하고 테스트해 봐야 사용 가능 여부를 판단할 수 있다.
예전의 RDBMS는 워낙 레퍼런스도 많고 벤더 지원도 있기 때문에 때에 따라 써야 하거나 쓰지 말아야 할 기능이 명확하지만, NoSQL의 경우는 제품도 많을 뿐더러 신기술이기 때문에 서적조차 드물다. 따라서 직접 분석하고 테스트해 보는 방법 밖에 없다.

5) 후보 NoSQL 선정 및 테스트

앞에서 언급한 바와 같이, 모델링한 데이터 구조를 효과적으로 실행할 수 있는 NoSQL을 찾아야 한다. 이를 위해 우선 NoSQL에 대한 구조 및 특성을 분석하고 실제로 부하, 안정성, 확장성을 테스트한 후에 가장 적절한 솔루션을 선택해야 한다.
경우에 따라서는 하나의 NoSQL이 아니라 여러 개의 NoSQL을 복합해서 사용할 경우도 있다. 트위터나 페이스북 같은 대규모 서비스들은 하나의 NoSQL만을 사용하지 않고 데이터의 성격과 업무 목적에 맞는 다수의 NoSQL을 선정해 사용한다.



6) 데이터 모델을 선정된 NoSQL에 최적화하고 하드웨어 디자인 진행

마지막으로 선정된 NoSQL을 기반으로 다시 데이터 모델을 최적화하고, 이에 맞는 애플리케이션 인터페이스 설계와 하드웨어에 대한 디자인을 진행해야 한다.
지금까지 간단하게나마 NoSQL의 데이터 모델링 절차에 대해 살펴봤다. NoSQL의 데이터 모델링 방식에서 다시 한 번 강조하지만, RDBMS가 데이터 자체의 관계를 중요시하고 데이터에서부터 출발한다면 NoSQL은 애플리케이션이 출력하고자 하는 데이터 출력 내용에 따라서 데이터 모델링이 완성된다. 또한 RDBMS와 역순으로 진행돼야 하고 데이터 중심이 아니라 애플리케이션 중심으로 모델링을 진행해야 한다. NoSQL은 신기술 분야이면서 오픈소스 진영이 주를 이루고 있으므로 기술 지원을 받기가 매우 어렵다. 물론 외국에는 NoSQL에 대해 유상 기술 지원을 해주는 업체들이 있긴 한데(Cassandra의 경우 Datastax가 기술 지원을 하고, Riak은 Basho, MongoDB는 10gen 등이 기술 지원을 한다), 국내 실정상 외국 엔지니어를 불러 기술 지원을 받기도 힘들 뿐더러 기술 지원 회사의 규모가 작기 때문에 숙련된 엔지니어를 필요할 때마다 부르기도 어렵다. 그리고 NoSQL 관련 서적도 그리 많지 않은 편이라서 공부하기도 어렵다. 해외에서 NoSQL 류를 쓰는 업체들은 스스로 내부 개발자들의 역량을 키워 공부하고 테스트해서 내재화된 기술을 기반으로 NoSQL을 운용하기 때문에, 단순하게 솔루션 도입 관점에서만 볼 것이 아니라 기술 내재화 관점에서도 NoSQL 도입을 고려해야 한다.



NoSQL 데이터 모델링 패턴


NoSQL 데이터 모델링 패턴은 Key/Value 저장 구조에 Put/Get 밖에 없는 단순한 DBMS에 대해 다양한 형태의 Query를 지원하기 위한 테이블을 디자인하기 위한 가이드이다.
특히 RDBMS에는 있는 Order by를 이용한 Sorting, group by를 이용한 Grouping, Join 등을 이용한 개체 간의 relationship 정의, 그리고 Index 기능들은 데이터를 쿼리하는 데 상당히 유용한 기능인데, NoSQL은 이러한 기능들을 가지고 있지 않기 때문에 NoSQL 내에서 데이터 모델링을 통해 이러한 기능들을 구현하는 방법에 대한 가이드를 제공한다.



기본적인 데이터 모델링 패턴


NoSQL의 데이터 모델링을 할 때 가장 기본적으로 많이 사용되는 패턴들이다.

● Denormalization
Denormalization은 같은 데이터를 중복해서 저장하는 방식이다. Denormalization을 하면, 테이블 간의 Join을 없앨 수 있다. NoSQL에서는 Join을 지원하지 않기 때문에 두 테이블을 조인해서 데이터를 가지고 오는 로직을 구현하려면, 각 테이블에서 데이터를 각각 가져와서 애플리케이션에서 합쳐야 하기 때문에 두 번의 IO가 발생한다.
Denormalization을 적용해 하나의 테이블에 Join될 데이터를 중복 저장하게 되면 한 번의 IO로도 데이터를 가지고 올 수 있다. 예를 들어 사용자 정보를 저장하는 User 테이블과 도시 이름을 저장하는 City 테이블이 있다고 하자,
column_img_753.jpg

이런 테이블 구조에서 “사용자별로 이름, 나이, 성별, 우편번호”를 출력하는 쿼리는 다음과 같다.

select u.name,u.age,u.sex,c.zipcode from user u,city c where u.city = c.city

이 테이블 구조를 그대로 NoSQL에서 구현하면 다음과 같이 구현해야 한다. User 테이블에서 city 값을 읽어온 후에 City 테이블에서 앞서 읽어온 city 값을 키로 해서 다시 zip code를 찾아와야 한다.

select $city,name,age,sex from user where userid=“사용자ID”
select zipcode from city where city=$city

이 예는 두 번 쿼리를 발생시킨다. 단순한 예이지만 Join을 해야 하는 테이블 수가 많을 때는 NoSQL로의 IO 수가 훨씬 더 늘어난다. 이 IO 수를 줄이려면 아예 처음부터 Join되어 있는 테이블을 하나 더 만들면 된다.
column_img_754.jpg

이런 식으로 새로운 테이블을 추가하면 된다. Denormaliza tion을 이용해 중복을 허용했을 경우에 장단점은 다음과 같다.

[장점]
· 성능 향상 : Join을 위해 몇 번씩 쿼리하지 않아도 되기 때문에 당연히 쿼리 성능이 향상된다.
· 쿼리 로직의 복잡도가 낮아짐 : Join에 대한 로직이 필요 없이 한 번에 데이터를 가지고 오기 때문에 쿼리 로직이 단순해진다.
[단점]
· 데이터 일관성 문제 발생 가능 : age, sex ,zip code 등을 insert하거나 update했을 때 User, City 테이블뿐만 아니라 UserZipCode 테이블도 업데이트해야 한다. 만약 같은 User 테이블을 업데이트하다가 에러가 났을 경우, UserZipCode 테이블은 업데이트가 안 된 상태이면 양쪽 테이블에 데이터 불일치성이 발생할 수 있다.
· 스토리지 용량 증가 : 데이터를 중복해서 저장하는 만큼 스토리지 용량이 증가된다.

사실 몇 가지 단점도 있지만 NoSQL의 경우 Join이 어렵고 애플리케이션적으로 구현한다고 해도 성능이 나오지 않거나 구현이 어려운 경우가 많기 때문에, Denormalization을 통한 중복은 상당히 효과적인 모델링 패턴이 된다.

● Aggregation
드롭박스처럼 파일을 저장하는 개인 스토리지 서비스가 있다고 가정하자. 이 서비스는 파일에 대한 정보를 저장하고 음악, 동영상, 사진 등의 파일 종류에 따라서 추가적인 메타 정보를 저장한다. ERD를 기준으로 개체를 표현해보면 <그림 8>과 같다.
column_img_755.jpg

NoSQL의 재미있는 특성 중의 하나가 Scheme-less 또는 Soft Scheme이라는 특성인데, RDBMS의 경우 테이블에 데이터를 넣을 때 반드시 테이블의 구조에 맞도록 넣어야 한다. 컬럼 수와 이름도 지켜야 하고 각 컬럼에 해당하는 데이터 타입도 준수해야 한다. 또한 모든 ROW는 같은 컬럼을 가져야 한다. 반면 NoSQL의 경우에는 Key만 똑같다면 각 Row는 제멋대로라도 상관없다. 꼭 같은 컬럼을 가질 필요도 없고 데이터 타입도 각기 달라야 한다. Value에 저장되는 Row 데이터들은 한 줄의 Row 구조를 가지기는 하지만, 전체 테이블에 대해서 그 Row 구조가 일치할 필요는 없다. 이처럼 데이터가 구조는 가지고 있지만 구조가 각각 다른 것을 허용한다고 해서 이를 Soft-Scheme 또는 Schemeless라고 한다. 이 특성을 이용하면 위의 데이터 모델을 하나의 테이블로 합칠 수 있다
column_img_756.jpg

1:N과 같은 복잡한 엔티티(Entity)들의 관계를 손쉽게 하나의 테이블로 바꿀 수 있고, 이는 결과적으로 Join의 수를 줄여서 Query 성능을 높이는 방법이 된다.

● Application Side Join
NoSQL은 Join이 거의 불가능하다(지원하는 제품도 있지만). 그래서 Denormalization이나 Aggregation 그리고 앞으로 설명할 데이터 모델링 패턴을 사용하는 것인데, 그래도 어쩔 수 없이 Join을 수행해야 할 경우가 있다. 이때는 NoSQL의 쿼리로는 불가능하고 NoSQL을 사용하는 클라이언트 Application 단에서 로직으로 처리해야 한다.
예를 들어 TABLE 1, TABLE 2라는 2개의 테이블이 있고 TABLE 1의 컬럼 중 하나가 Foreign Key로서 TABLE 2의 데이터를 가리키고 있다고 하자. Application Join을 하려면 TABLE 1에서 Primary Key로 한 Row를 읽어온 후에 TABLE 2를 가리키는 컬럼의 값을 Key로 해서 다시 TABLE 2에서 쿼리를 해온다.
column_img_757.jpg

Application Side Join은 Join이 필요한 테이블 수만큼 NoSQL로의 Request/Response IO가 발생하는 까닭에 다소 부담스럽긴 하지만, 반대로 Denormalization 등에 비해서는 스토리지 사용량을 절약할 수 있다.



확장된 데이터 모델링 패턴



● Atomic aggregation
NoSQL에서 고민해야 할 것 중 하나는 2개 이상의 테이블을 업데이트할 때 트랜잭션 관리에 대한 문제이다. <그림 13>과 같이 2개의 테이블을 업데이트하는 시나리오에서, TABLE 1을 업데이트한 후에 애플리케이션 로직이나 NoSQL의 장애로 인해 TABLE 2가 업데이트되지 않는 문제가 발생할 수 있다.
column_img_758.jpg

이 경우 데이터의 일관성 문제를 야기하는데, 이에 대한 해결 방안으로 생각할 수 있는 것은 TABLE 1, 2를 하나의 테이블로 합쳐 버리는 것이다.
column_img_759.jpg

하나의 테이블에 대해서는 NoSQL도 atomic operation(원자성)을 보장하기 때문에 트랜잭션을 보장 받을 수 있다. 구현 패턴 상으로는 Aggregation과 동일한데, Aggregation은 1:N의 relationship을 aggregate해서 Join을 없애기 위함이고, Atomic aggregation은 트랜잭션 보장을 통한 데이터 불일치성을 해결하기 위함이다.
예를 들어 사 3개 테이블로 분산되어 있을 때, 사용자를 생성하는 경우에는 이 3개의 테이블에 Insert를 해줘야 한다. 그러나 테이블이 3개로 분산되어 있기 때문에, 앞에서 언급한 장애 시 트랜잭션이 보장되지 않는다. 이를 해결하기 위해 Atomic aggregation 패턴을 적용해 이 3개의 테이블을 User 테이블의 필드로 집어넣게 되면, 하나의 테이블이기 때문에 장애나 에러로 인한 트랜잭션 불일치 문제를 방지할 수 있다.

column_img_760.jpg

● Index Table
NoSQL은 RDBMS와 달리 Index를 지원하지 않기 때문에, Key 이외의 필드를 이용해 Search를 하면 전체 Table을 Full Scan하거나 아니면 Key 이외의 필드에 대해서는 아예 Search가 불가능하다. 이 문제를 해결하기 위해 Index를 위한 별도의 Index Table을 만들어 사용할 수 있는데, 이는 사용 빈도가 매우 많은 방법이다. 예를 들어, 파일시스템을 NoSQL에 저장한다고 했을 때 파일 테이블은 <그림 16>과 같은 구조를 가진다.
column_img_761.jpg

여기서 특정 디렉터리에 있는 파일만을 리스트업하고 싶다면 별도의 Index Table을 <그림 17>과 같이 만들면 된다.
column_img_762.jpg

Cassandra나 Riak과 같은 일부 NoSQL은 제품 차원에서 Secondary Index라는 이름으로 Key 이외의 필드를 Index로 지정하는 기능을 가지고 있는데, 이 기능들은 아직까지 성숙되지 못해서 여기서 설명하는 Index Table을 사용하는 것보다 성능이 좋지 않다. 따라서 NoSQL에 있는 Secondary Index 기능을 사용할 예정이라면, 반드시 이 Index Table 패턴과 성능을 비교해보길 바란다. 당연히 Secondary Index를 이용하면 구현은 조금 편리해질 수 있지만 성능 차이는 클 수 있다.

● Composite Key
이 글을 읽으면서 눈치가 빠른 사람이라면 Key를 정의하는 데 “:” deliminator를 이용해 복합키를 사용하는 것을 눈치 챘을 것이다. NoSQL에서는 이 Key를 어떻게 정의하느냐가 매우 중요하다. 특히 Ordered KV Store의 경우에는 이를 이용해 order by와 같은 Sorting 기능이나 Grouping을 구현할 수 있다. Composite Key는 하나 이상의 필드를 deliminator로 구분지어 사용하는 방법으로 RDBMS의 복합키(Composite Primary Key)와 같은 개념으로 생각하면 된다. 단지 RDBMS의 경우에는 여러 개의 컬럼을 묶어서 PK로 지정하지만 NoSQL은 한 컬럼에 deliminator를 이용해 여러 개의 키를 묶어서 넣는다.
그럼 다음의 예제를 살펴보자(<그림 18> 참조). PC의 디렉터리를 Ordered KV Store에 저장한다고 하자. windows 하위 디렉터리를 가지고 올 때 “windows:etc”부터 쿼리해서 다음 row를 반복적으로 쿼리하고, Key가 windows로 시작하지 않을 때까지 읽어오면 windows 디렉터리의 하위 디렉터리를 모두 가지고 올 수 있다.
column_img_763.jpg

노드가 추가, 삭제되더라도 내부적으로 Sorting이 되기 때문에 문제없이 사용 가능하다. 단 Key 값을 선택할 때 주의해야 할 사항으로 ‘특정 서버로의 몰림 현상’을 들 수 있다. NoSQL은 특성상 N개의 서버로 구성된 클러스터이다. 그리고 데이터는 Key를 기준으로 N개의 서버에 나눠서 저장된다. 예를 들어 Key Range가 A~Z 26개라고 가정하고 클러스터의 서버 수가 26대라고 하면, 각 서버는 Key의 시작으로 사용되는 알파벳 키들의 데이터를 저장한다(1번은 A로 시작되는 Key 데이터들, 2번은 B로, 3번은 C로 등등).
이 Key 값을 “City:User Name”으로 했다고 가정하자, 우리나라 인구 5,000만 명 중에서 1/5인 1,000만 명이 서울에 살고 있다. 그래서 데이터 중 약 20%의 키는 “Seoul:xxx”로 시작할 것이고 26대의 서버 중에서 S로 시작하는 Key를 저장하는 1대의 서버는 20%의 부하를 처리하게 될 것이다. 서버가 26대이므로 각 서버는 1/26(약 3.8%)의 부하를 처리해야 하는데, 이건 예상치보다 5배 이상 많은 로드를 처리하기 때문에 성능 저하를 유발할 수 있다. 이런 이유로 Key를 선정할 때는 전체 서버에 걸쳐서 부하가 골고루 분산될 수 있는 Key를 선정하는 것이 좋다.

● Inverted Search Index
검색엔진에서 많이 사용하는 방법이다. 검색엔진은 사이트의 모든 페이지를 검색 로봇이 검색한 후 문서 내의 단어들을 색인해 URL에 맵핑하고 저장해놓는다.
column_img_764.jpg

검색은 단어를 키로 수행된다. 따라서 <그림 19>의 테이블 구조에서는 value에 검색 키워드들이 들어가 있어서 효과적인 검색을 할 수 없다. 이 검색 키워드를 Key로 해서 URL을 value로 하는 테이블을 다시 만들어 보면 <그림 20>과 같은 식으로 표현되고, 검색 키워드로 검색하면 빠르게 검색 키워드를 가지고 있는 URL을 찾아낼 수 있다.
column_img_765.jpg

이렇게 value의 내용을 Key로 하고, Key의 내용을 반대로 value로 하는 패턴을 Inverted Search Index라고 한다.

● Enumerable Keys
NoSQL 솔루션에 따라서 RDBMS의 Sequence와 같은 기능을 제공하는 것들이 있다. 이 기능들은 Key에 대해 자동으로 카운터를 올려주는 기능을 가지고 있다(예를 들어 첫 번째 Key는 1, 두 번째 Key는 2, 다음은 3, 4, 5 식으로 순차적으로 연속된 Key를 부여해주는 기능). 이 기능은 데이터에 대한 traverse 기능을 제공한다.



NoSQL 도입에는 큰 노력 요구돼


지금까지 몇 가지 NoSQL 적용 시 사용할 수 있는 데이터 모델링 패턴을 살펴봤다. 여기서 소개된 기본 패턴은 대부분의 NoSQL 솔루션에 적용할 수 있으며 확장된 모델링 패턴의 경우에는 NoSQL이 지원하는 기능이나 데이터 구조(KV, Ordered KV, Document 등)에 따라서 적용할 수 있다. 따라서 NoSQL을 이용한 시스템을 개발할 때는 다음을 꼭 기억할 필요가 있다.

- 데이터 모델링이 80% 이상이다. 선정한 NoSQL과 애플리케이션의 특성에 맞는 데이터 모델링에 집중하자.
- NoSQL은 어떤 솔루션이 좋다, 나쁘다가 없다. 어떤 솔루션의 특성이 어떻다는 것만 있기 때문에 반드시 데이터 모델과 내부 아키텍처 두 가지를 파악한 후에 애플리케이션의 특성에 맞는 NoSQL을 선정해야 한다.
- 하나의 NoSQL로 전체 데이터를 저장하려고 하지 마라. NoSQL은 데이터 구조가 매우 단순하지만, 애플리케이션의 경우 하나의 단순한 데이터 구조로 저장할 수 없는 데이터가 반드시 존재한다. RDBMS이나 다른 NoSQL과 혼용하고, 성능 면에서는 캐싱 솔루션과 혼용하는 것을 반드시 고려해야 한다.

NoSQL은 놀라온 성능과 확장성을 제공하지만 많은 연구와 노력도 필요하다. NoSQL이 오픈소스를 중심으로 사용되고 있지만 오픈소스라서 쉬운 게 아니다. 그만큼 많은 투자와 연구 노력이 필요한 만큼 신중하게 검토하고 도입을 결정하길 바란다.