본문 바로가기

데이터베이스(DA, AA, TA)/NoSQL

[Redis] 이것이 레디스다(1) - NoSQL

사용자 증가로 인한 서비스 중단의 원인이 DB 서버일때, 너무 많은 SQL 문 처리 요청을 받아 MySQL이 동시에 처리할 수 있는 한계치를 넘어섰고 그로 인해 응답시간이 길어질 수 있다. MySQL 서버의 응답시간 지연이 발생하여 톰캣 서버에서 DB 서버로의 네트워크 연결이 모두 끊어졌다. 이런 상황은 어떻게 대처해야 할까?

 

WAS를 추가로 설치하면 해결되던 때와는 상황이 달라졌다. 데이터베이스 서버의 하드웨어를 변경하려면 저장된 데이터를 새로운 서버로 다시 옮겨야 한다. 이와 같은 상황에서 선택할 수 있는 방법은 두가지다.

  - 방안1: 서비스를 멈추고 DB 서버에 더 많은 메모리와 CPU를 증설한 뒤 서비스를 시작한다.

  - 방안2: 서비스를 멈추고 더 좋은 성능 및 메모리를 가진 새로운 서버를 가져와서 기존 데이터를 새로운 서버로 옮긴다음 서비스를 시작한다.

 

MySQL 서버의 성능 문제로 서비스가 중단되면 하는 수 없이 기존 서버를 더 고가의 서버로 교체해야 하고, 그 작업 시간만큼 서비스가 중단될 것이다. 서비스를 멈추고 새로운 서버에 기존 서버의 데이터를 옮기고 데이터를 정리하려면 적게는 수시간에서 많게는 하루 이상이 소요된다. 언제까지 사용자가 늘어날때마다 이런 작업을 반복해야 하는 걸까?

 

단일 서버의 성능을 증가시켜서 더 많은 요청을 처리하는 방법을 스케일 업이라고 한다. 동일한 사양의 새로운 서버를 추가하는 방법을 스케일 아웃이라고 한다. 통상적으로 스케일 업을 적용하면 서비스의 중단이나 추가 하드웨어 비용이 발생한다.

 

서비스 중단 시간을 최소화하면서 스케일업을 실행하려면 기존 서버와 동일한 사양의 서버 한대에 동일한 데이터를 미리 준비하여 교체한 후 기존 서버에 추가적인 부품을 설치해야 한다. 이때 기존 서버와 동일하거나 비슷한 사양의 서버가 한대 더 필요하다. 기존 서버를 대체할 서버에 기존 데이터 및 환경을 동일하게 구축하는 등의 부갖거인 작업과 1회성 사용을 위한 서버를 구매해야 하마로 불필요한 추가 비용이 발생한다.

 

스케일업은 단일 하드웨어의 성능을 높이기 위하여 CPU, 메모리, 하드디스크를 업그레이드하거나 추가하는 것을 말한다. 스케일 업은 명확한 한계가 존재한다. 하나의 장비에 설치할 수 있는 CPU, 메모리, 디스크 수에는 한계가 있기 때문이다. 또한 소프트웨어 구조상 아무리 스케일 업을 한다고 해도 상승 가능한 물리적인 한계가 존재한다.

 

이에 반해서 대부분의 NoSQL은 처음부터 스케일 아웃을 염두에 두고 설계되었기 때문에 데이터의 증가나 요청량이 증가하더라도 동일하거나 비슷한 사양의 새로운 하드웨어를 추가하면 대응이 가능하다. 데이터가 적거나 또는 요청량이 적을때는 일반 RDBMS를 사용하더라도 서비스를 제공하는데 문제가 없다. 하지만 데이터 증가량을 측정하기 불가능하거나 서비스 요청량의 증가를 예측하기 어려운 상황에서는 NoSQL을 저장소로 사용하는 것이 현명한 선택이 될것이다.

 

멤캐시드와 레디스를 같은 캐시 캐시 시스템으로서 동등한 위치에서 비교하게 된것은 레디스가 멤캐시드와 동일한 기능을 제공하면서 영속성, 다양한 데이터 구조와 같은 부가적인 기능을 지원하기 때문이다. 또한 특정한 조건에서는 멤캐시드에 비해서 더 나은 성능을 보여주기도 한다. 이와 같은 이유로 레디스는 멤캐시드를 대체하는 솔루션으로 주목 받았다.

 

레디스는 모든 데이터를 메모리에 저장하고 조회한다. 즉, 인메모리 데이터베이스 솔루션이다. 빠른 성능은 레디스의 특징 중 일부부분에 지나지 않는다. 다른 인메모리 솔루션들과의 차이점 중 가장 특별한 점은 레디스의 '다양한 자료구조'다. 

 

NoSQL로서 키-값 타입의 저장소인 레디스의 주요 정보 및 특징은 다음과 같다.

  - 영속성을 지원하는 인메모리 데이터 저장소다.

  - 읽기 성능 증대를 위한 서버 측 복제를 지원한다.

  - 쓰기 성능 증대를 위한 클라이언트 측 샤딩을 지원한다.

  - ANSI C로 작성됐다. 따라서 ANSI C 컴파일러가 동작하는 곳이면 어디든 설치 및 실행이 가능하다.

  - 레디스 클라리언트는 대부분의 언어로 포팅되어 있다. ( http://redis.io/clients )

  - 많은 서비스에서 사용되고 있으며 성능적으로 검증된 솔루션이다.

  - 문자열, 리스트, 해시, 셋, 정렬된 셋과 같은 다양한 데이터형을 지원한다. 메모리 저장소임에도 불구하고 많은 데이터형을 지원하므로 다양한 기능을 구현할 수 있다.

 

레디스와 멤캐시드의 차이

 - 멤캐시드는 본질적으로 고성능 분산 메모리 객체 캐싱 시스템이지만, 원래는 동적 웹 서비스의 DB 부하를 경감시키는 것이 목적이었다.

 - 레디스는 오픈소서며, 향상된 키-값 저장소다. 값으로 문자열, 리스트, 해시, 셋, 정렬된 셋을 포함할 수 있기 때문에 종종 데이터 구조 서버로 지칭된다.

 

NoSQL에 멤캐시드는 포함되어 있지 않다. NoSQL은 데이터베이스(데이터 저장소)를 지칭하기 때문에 멤캐시드는 NoSQL이 아니다. 레디스는 고성능 인메모리 키-값 저장소로서 다섯 가지 데이터형(문자열, 리스트, 해시, 셋, 정렬된 셋)을 지원한다. 메모리 이외의 영구 저장소를 지원하므로 인스턴스의 재시작에 대한 cache warm up (캐시의 적중률을 높이기 위해서 미리 캐시 데이터를 생성하는 것)에 대한 고려가 필요하지 않다. 읽기를 위한 서버측 복제를 지원하며 쓰기 성능 증대를 위한 클라이언트측 샤딩을 지원한다.


NoSQL

NoSQL의 개념과 등장 배경, 사용 가능한 상황과 사용 방법에 대해 알아본다. 특히 분산 시스템의 근간이 되는 CAP 정리는 반드시 기억하는 것이 좋다. 

1. 정의

다음은 NoSQL을 경험한 사용자들의 단편적인 시각을 나열하고 있다.

  - NoSQL은 조인이 지원되지 않는 분산 데이터베이스다.

  - NoSQL은 데이터를 여러 서버에 분산하여 저장하며 스키마를 가질 수 없는 데이터베이스다.

  - 관계형 데이터베이스의 특징을 제거하고 만들어진 다른 모든 데이터베이스다.

  - 기존 방식의 관계형 데이터베이스의 한계를 극복한 새로운 형태의 확장성을 특징으로 하는 데이터 저장소다.

 

그런데 어떤 NoSQL은 조징닝 가능하며 또 어떤 NoSQL은 스키마를 가진다. RDBMS의 트랜잭션의 특징 중 하나인 ACID를 지원하는 NoSQL도 있다. 심지어 관계형 데이터베이스와 유사한 관계를 저장하는 NoSQL도 있다. 

 

마틴 파울러의 "NoSQL: 빅데이터 세상으로 떠나는 간결한 안내서"에는 NoSQL이 아래의 조건을 만족하는 데이터 저장소라고 기술되어 있다.

  - 대용량 웹 서비스를 위하여 만들어진 데이터 저장소

  - 관계형 데이터 모델을 지양하며 대량의 분산된 데이터를 저장하고 조회하는 데 특화된 저장소

  - 스키마 없이 사용 가능하나 느슨한 스키마를 제공하는 저장소

 

NoSQL은 저마자 쓰기/읽기 성능 특화, 2차 인덱스 지원, 자동 샤딩 지원과 같은 고유한 특징을 가진다. 여기서 말하는 특화라는 용어는 상대적인 쓰기 성능 대비 읽기 성능에 대한 지표다. 예를 들어 하둡 기반의 NoSQL인 HBase는 쓰기에 특화된 솔루션이다. 단일 시스템으로 구성된 HBase 클러스터는 초당 약 10만 쓰기 작업 또는 초당 약 5,000 읽기 작업을 수행할 수 있다. 즉, 읽기 작업보다 쓰기 작업이 더 빠르다. 일반적으로 NoSQL은 관계형 데이터베이스에 비해여 쓰기와 읽기 성능이 월등히 빠르다.

2. 탄생 배경

2010년 이전에는 기업용 애플리케이션을 작성할 때 사용할 데이터 저장소로 관계형 데이터베이스를 선택했다. 관계형 데이터베이스는 소프트웨어 개발자가 아닌 업무 담당자도 알고 있을 정도로  대중화된 솔루션이었다.

 

그런 와중에 성공한 글로벌 웹 서비스가 2000년대 중반부터 다수 등장하고, 글로벌 웹 서비스들은 기존의 관계형 데이터베이스를 사용하여 처리할 수 없을 만큼의 데이터를 생산하기에 이르렀다. 일반적으로 관계형 데이터베이스는 저장된 데이터의 양이 많아질수록 읽기/쓰기 성능의 저하가 일어난다. 특히 쓰기 성능의 저하가 심한데, 이는 대부분의 관계형 데이터베이스가 채택하고 있는 인덱스 처리 방법인 B트리의 한계에서 기인한다. 또한 관계형 데이터베이스는 중앙 집중식 데이터 관리 패턴에 해당하기 때문에 단일 하드웨어의 성능에 따라 전체 시스템의 성능이 결정된다.

 

이러한 이유로 오라클과 같은 상용 데이터베이스 개발사들은 분산환경에서 동작하도록 관계형 데이터베이스의 트랜잭션을 느슨하게 처리하는 솔루션을 개발했다. 이와 같은 솔루션을 사용하면 애플리케이션 복잡도가 증가하고 하드웨어와 소프트웨어 개발 비용 증가로 이어지게 된다. 게다가 관계형 데이터베이스의 가장 기본적인 특성들이 훼손되고 동시에 분산 환경에도 그다지 최적화되지도 못하는 딜레마에 빠지게 될 수도 있다. 관계형 데이터베이스 진영에서 기존의 관계형 데이터베이스 시스템을 개량하여 분산 환경에 대처하기 위한 솔루션을 개발하고 있을 때, 한편에서는 데이터를 다른 관점에서 바라보고 처리하는 기술에 대한 연구가 활발하게 진행되고 있었다.

 

구글, 페이스북, 아마존과 같이 성공한 글로벌 서비스 업체들은 더이상 관계형 데이터베이스만으로는 서비스 트래픽을 감당하기 어렵다고 판단했다. 특히 구글의 빅테이블, 아마존의 다이나모와 같은 논문 및 자료는 데이터를 실시간으로 분산처리하는 개념을 세상에 알려주었다. 시간이 지남에 따라 이 개념들을 토대로 실시간 분산 처리를 위한 오픈소스 솔루션들이 속속 개발되었고, 이와 같은 노력의 결과물이 NoSQL이다. NoSQL은 분산환경에서 대량의 데이터를 빠르게 처리하기 위해서 한두 가지 단점을 가진채로 개발됐다.

 

NoSQL은 관계형 데이터베이스가 제공하는 쿼리와 트랜잭션 같은 편의 기능을 제공하지 않으며, 제공하는 데이터의 일관성 레벨도 NoSQL별로 다르다. 또한 솔루션을 도입하기 위해서 해당 NoSQL의 설계 사상과 내부구조를 파악해야 하며 종류가 다양하기 때문에 각각의 NoSQL에 대한 도입 사례가 많지 않다. 그럼에도 불구하고 NoSQL을 선택하는 이유는 하드웨어 추가에 따른 성능의 선형 증가를 얻을 수 있다는 장점 때문이다. 또한 다양한 종류의 NoSQL이 존재하므로 선택의 폭이 넓다.

 

NoSQL의 탄생과 더불어 일각에서는 전통적인 데이터베이스 중심의 애플리케이션 개발에서 서비스 중심의 개발이라는 패러다임 전환이 일어났다. 이런 패러다임의 전환은 데이터베이스를 중앙 데이터 저장소가 아닌 서비스 제공의 도구로 바라보는 계기가 됐다.

 

NoSQL은 대량의 데이터를 빠르게 처리하기 위해서 메모리에 임시 저장하고 응답하는 등의 방법을 사용한다. 또한 동적인 스케일 아웃을 지원하기도 하며, 가용성을 위하여 데이터 복제 등의 방법으로 관계형 데이터베이스가 제공하지 못하는 성능과 특징을 제공한다. NoSQL은 매우 자유도가 높은 솔루션으로, 스키마 없이 데이터를 저장하고 조회할 수 있으며 실시간으로 시스템의 확장과 축소를 지원하기도 한다. 또한 시스템 정지 없이 저장소 소프트웨어의 업게링드를 수행할 수도 있다.

 

따라서 이런 다양한 특징을 파악하고 적절하게 사용하려면 많은 노하우가 필요하다. 결국 사용자의 능력에 따라 같은 솔루션을 사용하더라도 성능과 비용에서 많은 차이가 발생한다. 새로 도입하는 NoSQL 솔루션이라면 서비스에 필요한 기능을 제공하는지 뿐만 아니라 반드시 해당 NoSQL의 설계 사상과 내부구조를 파악하는 작업이 선행되어야 한다.

3. CAP 정리

CAP 정리란, 이론 컴퓨터 과학 분야에서 분산 컴퓨터 시스템을 사용하는 데 사용되는 이론이다. '일관성(Consistency), 가용성(Availability), 분할 허용성(Partition Tolerance) 모두를 동시에 지원하는 분산 컴퓨터 시스템은 없다'라고 정의되어 있다. 분산 시스템을 이해하기 위해 간단한 예를 들어본다. 만약 기업용 애플리케이션 개발자라면 웹서버와 DB 서버를 동일한 시스템에 설치하지는 않을 것이다. 일반적으로 웹서버와 DB서버는 각각 다른 하드웨어에 설치한다. 이와 같이 단일 시스템이 아닌 '다중 시스템 환경에서 소프트웨어가 작동하는 것'을 분산 컴퓨팅이라고 한다.

 

대부분의 NoSQL은 기본적으로 분산 환경에서 잘 동작하도록 설계되어 있다. 즉, 동일한 성격의 데이터가 물리적으로 다른 하드웨어에 저장되고 조회된다. 이때 각 NoSQL은 일관성, 가용성, 분할 허용성 가운데 두가지 속성만을 지원하며 나머지 한 속성은 특정 조건에서만 만족한다. 이것이 CAP 정리의 핵심이다.

 

CAP 정리에 기초한 세 가지 속성 중에서 어떠한 두 속성을 지원하는지에 따라서 NoSQL의 특징이 달라진다. CAP 정리를 이해하기 위해서는 분산 시스템의 용어에 대한 이해가 우선되어야 한다.

위 그림은 분산 시스템을 구성하는 개별 요소들을 나타내고 있다. 분산 시스템을 구성하는 각각의 하드웨어 또는 소프트웨어를 노드라고 부르며, 동일한 기능을 수행하는 노드들의 모음을 클러스터라고 한다. 분산 시스템은 하나 혹은 그 이상의 다중 클러스터로 구성될 수 있다. 다중의 클러스터에서 각 클러스터는 동일 지역 혹은 지역적으로 떨어진 위치에 존재할 수 있다. 이때 각 클러스터 간의 연결을 네트워크를 기반으로 한다. 이 내용을 기반으로 CAP 정리의 세가지 특성을 살펴본다.

3.1. 일관성

일관성은 동시성 또는 동일성이라고도 하며 '다중 클라이언트에서 같은 시간에 조회하는 데이터는 항상 동일한 데이터임을 보증하는 것'을 의미한다. 관계형 데이터베이스로 설명하면 '클라이언트 A가 특정 컬럼에 저장된 데이터값 300을 500으로 변경한 직후에 다른 클라이언트 B가 조회한 해당 컬럼의 데이터값은 항상 500이어야 한다'라는 조건이다.

 

예를 들어 어제 블로그에 글을 등록하고 오늘 낮 1시에 수정했다고 가정한다. 여러분이 작서한 글을 오늘 1시 1분에 다른 사람이 보았을때 어제 등록된 글의 내용이 보이는 것이 정상인가? 아니면 처음 등록한 글이 보이는 것이 정상인가? 어찌 보면 매우 당연한 이야기일 수도 있지만 일관성을 지원하지 않는 NoSQL을 사용한다면 처음 등록한 글이 보일 수도 있다.

 

일관성은 관계형 데이터베이스가 지원하는 가장 기본적인 기능이다. 하지만 NoSQL에서는 빠른 분산 처리를 위하여 일관성을 희생하기도 한다. 위에서 설명한 이론에 따라서 가용성과 분할 허용성을 지원하는 NoSQL은 일관성이 느슨하게 처리되어 위와 같은 상황이 발생할 수 있다. 대부분 시간이 지남에 따라 수정된 내용이 다른 노드로 전파되어 반영된다. 데이터의 변경이 다른 노드로 전파되는 상황이다.

1. 클라이언트1이 노드 A에 접소하여 key1에 300이라는 값을 저장한다.

2. 노드 A에서 '데이터 변경 전파' 이벤트가 발생한다.

3. 데이터 변경 전파 이벤트에 의해 노드 B에 key1과 값 300이 복제된다.

4. 클라이언트1이 노드 A에 저장된 key1의 값을 500으로 변경한다.

5. 클라이언트2가 노드 B에 접속하여 key1의 값을 조회한다. 노드 A에 저장된 key1의 값이 변경된 이후에 데이터 변경 전파 이벤트가 발생하지 않았기 때문에 처음 복제된 값인 300이 조회됐다.

6. 데이터 변경 전파 이벤트가 발생한다.

7. 데이터 변경 전파 이벤트에 의해 노드 B의 key1의 값이 500으로 변경된다.

8. 클라이언트2에서 노드 B에서 key1의 값을 조회한다. 데이터 변경 전파 이벤트 이후에 데이터를 조회했으므로 변경된 값인 500이 조회된다.

 

위와 같은 방법을 사용하여 일관성을 유지하는 것을 최종 일관성(Eventual Consistency) 또는 궁극적인 일관성이라고 하며, 시간이 지나감에 따라 결과적으로 일관성이 보장되는 상황을 일컫는다. 위에서 표현한 데이터 변경 전파 이벤트는 NoSQL에서 실제로 존재하는 용어는 아니다.

 

각 NoSQL은 분산 노드 간의 데이터 동기화를 위해서 두가지 방법을 제공한다. 첫번째로 데이터의 저장 결과를 클라이언트로 응답하기 전에 모든 노드에 데이터를 저장하는 동기식 방법이 있다. 동기식 방법은 모든 노드의 데이터 저장이 완료되는 시간 동안 클라이언트에게 저장 결과를 돌려줄 수 없으므로 느린 응답시간을 보이지만, 강한 데이터의 정합성을 보장한다. 두번재로 메모리나 임시 파일에 기록하고 클라이언트에 먼저 응답한 다음, 특정 이벤트 또는 프로세스를 사용하여 노드로 데이터를 동기화하는 비동기식 방법이다. 비동기식 방법은 클라이언트에게 빠른 응답을 줄 수 있지만, 쓰기 노드에 장애가 발생하였을때 데이터를 잃어버릴 수 있는 단점이 있다.

 

NoSQL에서 엄밀한 일관성은 최종 일관성에 반대되는 개념이다. 분산 시스템에서 일관성을 유지하기 위해서는 응답시간(latency)의 희생이 따른다. 이와 같은 응답시간의 지연을 방지하기 위해서 일관성과 응답시간의 적절한 타협점을 찾기도 한다. 널리 알려진 NoSQL 중의 하나인 카산드라를 예로 들면 성능과 일관성의 적절한 타협점을 사용자가 지정할 수 있는 일관성 레벨을 지원한다. 이 값의 설정에 따라서 응답 시간을 조절할 수 있다.

 

많은 NoSQL 솔루션을 읽기와 쓰기의 성능 향상을 위해 데이터를 메모리에 임시로 기록한 다음 클라이언트에 응답하고 백그라운드 스레드 (혹은 프로세스)로 해당 데이터를 디스크에 기록한다. 이런 처리 방식의 장점은 빠른 응답 속도에 있으며, 데이터의 변경에 따른 수정 비용이 적게 든다는 장점이 있다. 반면 정전과 같은 하드웨어 장애 발생시에는 데이터 유실이 발생할 수 있다. 카산드라와 HBase에서는 이런 데이터의 유실을 방지하기 위하여 메모리에 저장하기 전에 커밋 로그 및 WAL(Write Ahead Log) 파일에 먼저 정보를 기록하여 데이터의 유실을 방지하는데, 레디스도 이와 유사한 기능인 AOF(Append Only File)를 사용한다.

 

카산드라의 일관성 레벨

가용성과 분할 허용성을 지원하는 카산드라는 최종 일관성을 지원한다. 최종 일관성이란 시간이 지남에 따라서 최종적으로 일관성이 유지되는 일관성을 말한다. 또한 카산드라는 설정값을 조절하여 강한 일관성을 지원할 수 있다. 카산드라에서 설정 가능한 주요 일관성 레벨은 다음과 같다.

  - One: 하나의 노드로부터 읽기 또는 쓰기의 성공 응답을 받으면 클라이언트로 응답한다.

  - Quorum: '설정된 복제 계수 / 2 + 1'개의 노드로부터 읽기 또는 쓰기 성공 응답을 받으면 클라이언트로 응답한다.

  - All: '설정된 복제계수'의 노드로부터 읽기 또는 쓰기의 성공 응답을 받으면 클라이언트로 응답한다.

 

'설정된 복제계수'는 카산드라에서 사용되는 속성으로서 데이터 복제본을 몇개나 저장할지 결정한다. 상용 서비스에서는 복제본 3개가 권장된다. 일관성 레벨을 Quorum으로 설정하면 위의 공식에 따라서 2개의 노드에서 데이터 처리 응답을 받고 나서 클라이언트로 응답한다. 일관성 레벨이 One일때 가장 빠른 응답 시간을 가지며, 일관성 레벨이 All일 때 가장 느린 응답을 가지게 된다. 이와 같인 응답시간(=성능)을 희생하여 강한 일관성을 지원할 수 있다.

3.2. 가용성

가용성이란 '모든 클라이언트의 읽기와 쓰기 요청에 대하여 항상 응답이 가능해야 함을 보증하는 것'이며 내고장성이라고도 한다. 내고장성을 가진 NoSQL은 클러스터 내에서 몇개의 노드가 망가지더라도 정상적인 서비스가 가능하다. 예를 들어 물리적인 3개의 데이터 저장소를 가지는 클러스터에서 하나의 데이터 저장소가 제거되더라도 전체 클러스터에 영향을 주지 않는다면 가용성을 가진다고 말할 수 있다. 이는 시스템 아키텍처 설계에서 말하는 고가용성(High Availabity)과 유사한 개념으로 볼 수 있다.

 

몇몇 NoSQL은 이와 같은 가용성을 보장하기 위해 데이터 복제를 사용한다. 동일한 데이터를 다중 노드에 중복 저장하여 그중 몇 대의 노드가 고장 나도 데이터가 유실되지 않도록 하는 방법이다. 가용성을 지원하는 NoSQL은 부분 노드 장애 상황에서도 데이터의 조회와 저장에 대한 처리가 가능하다. 5개의 노드로 구성된 클러스터를 표현한 그림을 살펴보자. 

하나의 데이터가 세 개의 노드에 분산 저장되는 형태를 보여준다. 노드 다섯 개 중 두 개에 장애가 발생했는데도 데이터를 정상적으로 조회하고 수정할 수 있다. 즉, 노드 2와 노드 4에 장애가 발생하더라도 데이터"2"을 노드 3에서 읽을 수 있기 때문에 데이터의 유실이 발생하지 않는다. 가용성을 위한 데이터 중복 저장 방법에는 동일한 데이터를 가진 저장소를 하나더 생성하는 마스터-슬레이브 복제 방법과 데이터 단위로 중복 저장하는 피어-투-피어 복제 방법이 있다.

 

마스터-슬레이브 복제는 관계형 데이터베이스 시스템에서 고가용셩얼 지원하기 위한 솔루션으로 사용되기도 한다. 가용성을 설명하면서 빠지지 않는 단어가 단일 고장점 SPOF(Single Point Of Failure)이다. 단일 고장점이란 시스템을 구성하는 개별 요소 중에서 하나의 요소가 망가졌을때 시스템 전체를 멈추게 만드는 요소를 말한다. 단일 고장점을 가진 NoSQL은 자체적으로 가용성을 지원하지 못하기 때문에 이를 지원하기 위해 별도의 솔루션을 사용하기도 한다.

 

단일 고장점

단일 고장점을 가진 솔루션들은 분산 환경에서 전체 서비스가 중단되는 심각한 문제가 발생할 수 있기 때문에 기피 대상이다. 예를 들어 대표적인 컬럼 모델 NoSQL인 HBase는 단일 고장점을 가지고 있다. HBase는 하둡이 제공하는 HDFS에서 동작하는데, 하둡의 네임노드가 단일 고장점이다. 네임노드는 하둡의 데이터 노드에 저장된 파일의 위치 정보를 가지고 있다. HBase를 사용하면서 가용성을 지원하기 위해서 리눅의 HA 솔루션과 DRDB와 같은 솔루션을 적용하여 가용성을 확보하기도 한다.

3.3. 네트워크 분할 허용성

Partition tolerance라는 용어를 흔히들 분할 허용, 분산 가능, 파티션 허용, 부분 결함 허용 등으로 번역하는데 충분해 보이지 않는다. 오픈소스 분산 파일 시스템 하둡의 창시자가 이끄는 회사인 클라우데라의 블로그에 게시된 포스팅에서 Partition tolerance를 네트워크 분할 허용성(Tolerance to Network Partitions)으로 설명하고 있다. 즉, 지역적으로 분할된 네트워크 환경에서 동작하는 시스템에서 두 지역 간의 네트워크가 단절되거나 네트워크 데이터의 유실이 일어나더라도 각 지역 내의 시스템은 정상적으로 동작해야 함을 의미한다.

 

CAP 정리를 구성하는 세 가지 요소인 일관성, 가용성, 분할 허용성에 관하여 알아보았다. CAP 정리와 각 NoSQL이 지원하는 특징을 한장의 그림으로 표현하면 다음과 같다.

CA 영역에 있는 RDBMS는 일관성과 가용성을 지원한다. 두가지 속성만을 지원한다는 의미는 아니다. 선택된 두 가지 속성을 지원하기 위해 나머지 한 속성에 대한 희생이 필요하다는 의미다.

 

일관성, 가용성, 분할 허용성 중에서 어떤 두가지 속성을 지원하는지에 따라서 NoSQL의 특징이 달라지게 된다.


NoSQL의 분류

NoSQL은 데이터의 저장 방식에 따라 분류되기도 한다. NoSQL은 데이터 접근을 위해 키를 사용하는데, 키에 저장된 값의 데이터 모델에 따라서 분류하는 방법이다. 영문표기는 Document oriented, Document based와 같이 혼용해서 사용한다. 데이터 저장 모델에 따른 분류이므로 문서 모델이 더 적절한 표현이다.

 

저장소는 크게 키-값 모델, 문서 모델, 컬럼 모델, 그래프 모델로 분류할 수 있다. 각 데이터 모델에 해당하는 NoSQL에 대한 공통적인 특징과 데이터의 논리적인 저장구조에 대하여 알아본다. 더불어 각 모델별 NoSQL의 적절한 사용 예를 알아본다. 설명하는 내용이 각 분류에 해당하는 모든 NoSQL의 특징과 정확히 부합하지 않을 수 도 있다. 각 모델에 대한 저장 개념을 이해하고, NoSQL 분류에 따른 특징을 비교하는 참고 자료로 이해하는 것이 좋다.

1. 키-값 모델 NoSQL

키-값 모델 NoSQL은 가장 기본적인 형태의 NoSQL이며, 키 하나로 데이터 하나를 저장하고 조회할 수 있는 단일 키-값 구조를 가진다. 관계형 데이터베이스와 비교하자면 주 키 하나와 일반 필드 하나를 가진 테이블을 들 수 있다.

 

공통 특징

키-값 모델 NoSQL에 저장 가능한 데이터의 종류는 각 NoSQL별로 상이한데, 일반 문자열 만을 저장 가능한 구조가 잇는가 하면 Blob 형태의 데이터를 저장할 수 있는 NoSQL도 있다. 대부분의 키-값 모델 NoSQL은 단순한 저장구조로 인하여 복잡한 조회 연산을 지원하지 않는다. 또한 고속 읽기와 쓰기에 최적화된 경우가 많다. 다른 모델의 NoSQL과 비교하여 API가 단순하기 때문에 처음 NoSQL을 접하는 사용자가 쉽게 이해하고 사용할 수 있다. 대부분의 키-값 모델 저장소는 저장된 데이터에 대한 검증이나 데이터의 내용에 기반한 조회를 지원하지 않으며, 저장되는 값을 단지 의미 없는 바이너리 데이터로 처리한다. 

 

키-값 모델 NoSQL로는 레디스, 리악, 디나모, 볼드모트 등이 있다.

  - 레디스: 다양한 자료구조를 저장하고 조회할 수 있으며 다양한 종류의 조회 방법을 지원하는 인메모리 NoSQL이다. C 언어로 구현되어 있다.

  - 리악: 단일 고장점을 가지지 않는 아키텍처와 맵 리듀스를 지원하는 키-값 모델 NoSQL이다. 값에 태그를 저장하여 보조 인덱스 생성이 가능하다. 카산드르와 유사한 클러스터링 아키텍처를 지원하며, 최종 일관성을 지워한다.

  - 디나모: 범위 조회가 가능한 키-값 모델 NoSQL이다. 아마존에서 개발하고 서비스 중인 상용 서비스, 서비스 종류에 따라 다른 과금 체계를 가진다. 데이터는 SSD에 저장되며, 맵리듀스를 사용한 프로그래밍이 가능하다.

  - 볼드모트: 링크드인에서 사용하고 있는 키-값 모델 저장소다. 단일 고장점을 가지지 않으며, 아ㅓ마존의 다이나모 설계를 구현한 오픈소스 솔루션이다. 볼드모트 공식 문서에 따르면 초당 1~2만 건을 처리할 수 있다.

 

리악과 레디스는 키-값 저장소로 분류되지만 공통 특징 이외의 부가적인 기능도 제공하다. 리악은 값에 태그를 추가하여 보조 인덱스를 지원하며, 레드스는 키에 해시나 리스트와 같은 데이터 구조를 저장하여 값에 대한 조회를 지원하다. 이와 같은 개별 NoSQL의 특징에 따라서 저장된 값의 비교를 처리할 수 있는 키-값 모델 NoSQL도 있다.

 

데이터 저장 방법

다음은 키-값 모델 NoSQL의 데이터의 저장 개념을 보여준다.

위와 같이 단일 키에 단일 데이터를 저장하는 간단한 구조로 인하여 주로 단순한 정보의 빠른 저장과 조회를 제공한다. 상황에 따라 키-값 모델 NoSQL에 복잡한 정보를 저장하기 위해 키에 정보를 포함하도록 하는 '키 설계' 기법을 사용하기도 한다. 키 설계에 대한 자세한 내용은 '레디스 키 설계'를 참조한다.

 

데이터 변환

관계형 데이터베이스의 데이터를 일반적인 키-값 저장소에서 다루는 방법을 알아본다. 키-값 모델 NoSQL에서 사용자의 주문 정보 조회가 가능하도록 설계한 예제다.

위 그림은 사용자 주문 정보를 조회할 수 있도록 저장한 결과다. user:1:orderlist, user:3:orderlist에는 각 사용자의 구매 목록이 저장되어 있고 order:1, order:2, order:3은 구매 목록에 대한 세부 주문 내용을 저장하다. 사용자와 구매 정보의 관계를 키의 이름으로 표현하고 저장된 값에 구매 목록을 저장했다. 이 같은 관계형 데이터베이스의 데이터를 NoSQL로 전환하려면 관계형 데이터베이스에 저장된 관계 정보를 키의 이름으로 표현해야 한다.

 

사용자1에 새로운 주문을 추가해본다. 사용자1이 원두커피를 구매하고 배송지는 강남구 신사동이라 가정한다.

1. 새로운 구매 정보를 생성한다. 마지막 구매 정보 ID가 3이므로 새로운 구매 정보 ID는 4이다. 구매 정보를 저장할 order:4 키를 생성하여 상품 정보와 배송지 정보를 저장한다.

2. 사용자1의 구매 목록에 새로운 구매 정보를 추가하기 위해서 user:1:orderlist 키를 조회한다.

3. 2.의 결과에서 이전에 저장한 구매 목록이 존재하면 맨 뒤에 새로운 값을 추가하고, 그렇지 않으면 새로운 값을 생성한다.

 

전부 2회의 저장 연산과 1회의 조회 연산으로 사용자의 구매 정보가 저장됐다. 주문이 추가된 이후의 데이터는 다음과 같다.

 

이번에는 사용자1이 구매한 상품의 목록을 조회한다. 사용자의 구매 목록이 저장된 user:1:orderlist 키를 조회한다. 조회한 구매 목록을 사용하여 주문 정보를 조회하려면 키를 조합해야 한다. 주문을 표현하는 order와 주문번호를 조합한다. 키 order:1과 order:3, order:4의 값을 조회하면 사용자1의 모든 주문 내용을 확인할 수 있다. 4회의 조회 연산으로 사용자 1의 모든 주문 내용을 조회했다. 한 사용자의 구매 목록이 많을 때는 더 많은 연산이 필요하게 되어 NoSQL의 장점을 살리지 못하게 된다. 이런 상황에서는 키-값 모델 NoSQL이 아닌 다른 모델의 NoSQL을 선정하는 것이 타당하다.

 

적절한 사용처

키-값 모델 NoSQL의 적절한 사용처로는 사용자의 프로필 정보, 웹서버의 클러스터를 위한 세션 정보, 장바구니 정보, URL 단축 정보 저장 등이 해당된다. 위의 예제들은 모두 단일 연산에 의하여 처리를 완료하거나 취소할 수 있기 때문이다.

 

대부분의 키-값 모델 NoSQL은 데이터 자체의 값을 기준으로 검색하는 기능을 지원하지 않기 때문에 우편번호를 저장하고 30개씩 분할하여 출력하는 서비스에는 적절하지 못하다. 또한 복잡한 다중연산이 필요한 서비스에도 어울리지 않는다. 키-값 모델 NoSQL은 단일키 처리만을 지원하기 때문이다. 또한 하나의 서비스 요청에 다수의 데이터 조회 및 수정 연산이 발생하면 트랜잭션 처리가 불가능하여 데이터 정합성을 보장할 수 없다. 복잡한 연산 역시 다수의 데이터 조회와 저장을 수반하므로 성능에 악영향을 미친다.

2. 문서 모델 NoSQL

문서 모델 NoSQL 저장소는 키-값 모델을 개념적으로 확장한 구조다. 실제로 문서 모델 NoSQL과 키-값 모델 NoSQL은 경계가 모호한 경우도 있다. 문서 모델은 하나의 키에 하나의 구조화된 문서를 저장하고 조회한다. 논리적인 데이터의 저장과 조회 방법이 관계형 데이터베이스와 유사하기 때문에 NoSQL을 처음 도입하는 프로젝트에서 많이 선택한다.

 

공통 특징

문서 모델 NoSQL이 동작하는 방식은 마치 URL을 이용하여 웹서버에 저장된 HTML 문서를 조회하는 것과 유사한데, URL이 키에 해당하고 HTML 문서가 저장된 데이터에 해당된다. 이와 같은 사용 패턴으로 인하여 전통적인 Content Management System과 혼동하는 경우가 있다. 문서 모델 NoSQL이 의미하는 구조화된 문서란 JSON, BSON, XML과 같이 문서가 이미 구조를 정의하고 있는 것을 말한다.

 

문서 모델 NoSQL의 키는 문서에 대한 ID로 표현된다. 또한 저장된 문서를 콜렉션으로 관리하며, 문서 저장과 동시에 문서 ID에 대한 인덱스를 생성한다. 문서 ID에 대한 인덱스를 사용하여 O(1) 시간 안에 문서를 조회할 수 있다. 저장된 문서의 형태에 따라 키 이외의 2차 인덱스를 지원하기 때문에 값을 비교한 조회가 가능하다.

 

키-값 및 컬럼 모델 NoSQL에 비하여 상대적으로 많은 종류의 기능을 제공하며, 관계형 데이터베이스와 유사한 검색 조건을 포함한 쿼리를 처리할 수 있다. 이런 이유로 많은 사용자가 문서 모델 NoSQL을 선호한다.

 

대분의 문서 모델 NoSQL는 B트리 인덱스를 사용하여 2차 인덱스를 생성한다. B트리는 크기 커지면 커질수록 새로운 데이터를 입력하거나 삭제할 때 성능이 떨어지게 된다. 이런 이유로 B트리를 사용하는 문서 모델 NoSQL은 읽기와 쓰기의 비율이 7:3 이상 일때 가장 좋은 성능을 보인다. 문서 모델 NoSQL에는 MongoDB, 카우치베이스, 테라스토어, 레이븐DB 등이 있다.

  - MongoDB: 자동 샤딩을 지원하는 NoSQL

  - 카우치베이스: B트리의 단점을 보완한 수정된 B트리 인덱스를 사용하며, 자체 클러스터 모니터링 기능이 뛰어난 문서 모델 NoSQL이다. 내부적으로 맵 리듀스를 사용하여 동작한다.

  - 테라스토어: Quartz Scheduler를 만든 테라코타 사에서 만들었다. 일관성을 희생하지 않으면서 동적 확장과 자동 샤딩을 지원하는 문서모델 NoSQL이다.

 

데이터 저장 방법

다음은 JSON 문서를 저장하고 있는 문서 모델 NoSQL의 저장구조를 나타낸다.

문서 모델 NoSQL은 위와 같이 구조화된 문서 데이터를 저장하며, 문서 안에 문서를 포함하는 내포 관계를 정의하거나 문서의 참조를 지정할 수도 있다. 관계형 데이터베이스는 위와 같은 데이터를 젖아하기 위해서 반드시 skills 컬럼을 정의하고 null 값을 넣어야 하지만, 문서 모델 NoSQL은 항상 고정된 필드를 가진 구조의 문서를 저장할 필요가 없다. 문서 모델 NoSQL에서 데이터에 대한 검색을 시도할 때, 조회 조건에 지정된 필드가 존재하지 않는 문서는 조회 대상에 포함되지 않는다.

 

데이터 변환

관계형 데이터베이스에 저장된 데이터를 문서 모델 NoSQL에서 어떻게 다루는지 알아본다. 다음은 관계형 데이터를 문서 모델 NoSQL에서 사용자 주문 정보를 조회할 수 있도록 설계한 예제이다.

위에서는 키 하나에 여러 정보를 포함하는 문서 구조를 저장했다. user:1:orderlist 키는 사용자1의 주문 목록을 젖아하고 있으며, 문서 내부에 개별 주문 정보가 포함되어 있다.

 

사용자1의 새로운 주문 정보를 생성하고 조회해본다. 사용자1은 원두커피를 구매했고 배송지는 강남구 신사동이며, 주문번호는 4라고 가정한다.

 (1) 주문 목록이 저장된 user:1:orderlist 키의 order 필드를 조회한다.

 (2) 주문번호 4를 가진 주문 정보를 생성한다. 다음으로 (1)의 결과에 이전 주문 목록이 존재하면 주문 정보를 추가하고, 존재하지 않으면 주문 정보를 입력한다.

 

단 1회의 조회 연산과 1회의 입력 연산으로 모든 처리가 완료된다. 저장된 값의 뒤에 새로운 값을 추가하는 append 명령을 지원하는 NoSQL 이라면 단 1회의 연산만으로 처리를 완료할 수 있다. 키-값 모델 NoSQL에 비하여 저장구조는 복잡하지만 데이터 처리 연산은 상대적으로 간단하다.

 

적절한 사용처

문서 모델 NoSQL은 B트리의 특성으로 인하여 한 번 작성되면 자주 변하지 않는 정보를 저장하고 조회하는데 적합하다. 예를 들어 중앙 집중식 로그 저장, 타임라인 저장, 통계 정보 저장 등이 이에 해당된다. 단, 조회시 특정 수량을 기준으로 잘라서 조회하는 기능 등에는 알맞지 않다.

 

문서 모델 NoSQL이 지원하는 2차 인덱스와 상세한 쿼리 기능으로 인하여 문서 모델 NoSQL로 관계형 데이터베이스를 대체하려는 시도들이 있었는데, 이런 시도는 대체로 실패로 돌아갔다. 2차 인덱스와 검색 조건을 사용한 쿼리를 너무 신뢰한 나머지 관계형 데이터베이스의 스키마를 그대로 문서 모델 NoSQL로 옮기는 방법을 사용하기 때문이다. 문서 모델 NoSQL이 복잡한 조건의 쿼리를 지원하기는 하지만, 관계형 데이터베이스에서 지원하는 기능에는 아직 미치지 못한다.

 

2차 인덱스는 조회 기능의 편의성을 제공하기도 하지만, 쓰기 성능을 저하시키는 양날의 검과 같은 기능이다. 지속적인 데이터의 입력 상황에서는 인덱스가 설정되지 않은 컬렉션에 대한 쓰기 성능과 설정된 컬렉션에 대한 쓰기 성능은 최대 100배 정도의 차이가 난다.

3. 컬럼 모델 NoSQL

컬럼 모델 NoSQL은 하나의 키에 여러 개의 컬럼 이름과 컬럼 값의 쌍으로 이루어진 데이터를 저장하고 조회한다. 단일 키에 의한 단일 컬럼 및 범위 조회도 가능하다. 모든 컬럼은 항상 타임스탬프값과 함께 저장된다. 컬럼 모델 NoSQL에서 키는 로우키라 불린다.

 

NoSQL의 분류 중에서 가장 복잡한 저장구조를 가지는 NoSQL이다. 컬럼 모델 NoSQL은 컬럼 패밀리라는 조금 생소한 개념을 사용하여 컬럼 모델 NoSQL을 도입하려는 사용자들에게는 다소 멀게 느껴질 수 있다.

 

공통 특징

대부분의 컬럼 모델 NoSQL은 구글 빅테이블의 영향을 받아 개발됐다. 이러한 이유로 로우키(Row key), 컬럼키(Column key), 컬럼 패밀리(Column Family) 같은 빅테이블 개념이 공통적으로 사용된다. 저장과 조회의 기본 단위는 컬럼이다. 컬럼은 컬럼 이름과 컬럼값, 타임스탬프로 구성된다. 컬럼의 집합은 로우이며, 로우키는 각 로우를 유일하게 식별하는 값이다. 로우의 집합은 테이블 또는 키 스페이스가 된다.

 

컬럼 모델 NoSQL에서 컬럼 하나를 조회할 때는 행을 구분하는 로우키와 열을 구분하는 컬럼키를 사용한다. 마치 스프레드시트에서 X축, Y축에 따라 셀을 조회하는 방법과 동일하다. 이때 컬럼키를 생략하면 해당 로우에 저장된 모든 컬럼이 조회된다. 용어적인 설명으로는 관계형 데이터베이스의 구조와 크게 다를 것이 없어보인다. 하지만 몇가지 차이점이 있는데, 컬럼의 저장 방식과 컬럼의 타임스탬프에 관한 내용이다.

 

데이터 파일의 저장 방법

관계형 데이터베이스는 물리적으로 데이터를 저장할 때, 컬럼의 집합인 레코드를 기준으로 데이터 파일을 생성하지만, 컬럼 모델 NoSQL은 컬럼 패밀리를 기준으로 데이터 파일을 생성한다. 예를 들어 두 개의 컬럼 패밀리를 가진 로우를 저장하면 두 개의 물리적인 파일이 생성되어 저장된다. 이와 같은 이유로 하나의 로우에 저장된 모든 컬럼을 조회하는 것보다 로우키에 해당하는 컬럼 패밀리를 조회하는 것이 더 빠르다. 또한 하나의 로우에 많은 컬럼 패밀리를 지정하면 하나의 로우를 조회하기 위해서 더 많은 물리적 파일에 접근하게 되어 응답시간이 늘어나게 된다. 

 

타임스탬프

컬럼 모델 NoSQL은 저장된 컬럼에 대한 타임스탬프를 유지한다. 이미 저장되어 있는 컬럼과 동일한 로우키와 컬럼키를 가진 필드를 추가하면 최근의 타임스탬프를 가진 새로운 컬럼이 추가되어 동일한 이름을 가진 컬럼에 두 개의 값이 유지된다. 이때 조회 연산이 수행되면 가장 최근의 타임스탬프를 가진 데이터를 돌려준다. 또한 동일한 이름을 가진 컬럼을 몇 개까지 유지할 것인가에 대한 설정도 가능하며, 과거의 데이터도 조회할 수 있다.

 

컬럼 모델 NoSQL은 테이블의 모든 로우키가 사전식으로 정렬되어 저장된다. 따라서 시작키, 종료키 조건을 사용하여 범위 지정 조회가 가능하다. 마찬가지로 컬럼도 사전식으로 정렬되어 저장된다. 즉, A라는 컬럼 패밀리에 순서대로 ac, ab, a1 컬럼을 저장하면 a1, ab, ac 순서로 저장된다.

 

관계형 데이터베이스가 로우 하나를 저장할 때는 스키마 레벨에서 저장 가능한 필드 갯수가 고정된다. 반면, 컬럼 모델 NoSQL의 스키마는 컬럼 패밀리만을 지정하고 컬럼의 갯수나 이름을 지정하지 않는다. 즉, 데이터를 젖아할 때 컬럼 이름이나 갯수를 임의로 지정할 수 있다. 이런 구조로 인하여 느슨한 스키마를 제공하는 NoSQL이라 불린다. 컬럼 모델 NoSQL이 스키마 없이 사용된다고 알려져 있는데 이것은 잘못된 정보다. 데이터를 저장하려면 먼저 테이블의 이름과 테이블에 저장되는 컬럼 패밀리의 이름을 지정하는 스키마를 생성해야 한다. 키-값 모델 NoSQL과 문서 모델 NoSQL은 스키마 없이 사용된다.

 

컬럼 모델 NoSQL은 로우키 하나에 저장 가능한 컬럼 갯수에 제한이 없다. 즉, 하나의 로우에 수백만 개의 컬럼을 지정할 수 있다. 빠른 쓰기 성능을 위해서 메모리와 커밋로그에 데이터를 기록하고 클라이언트로 응답하고 지정한 임계값보다 메모리의 크기가 커지면 영구 저장소에 저장하는 방법을 사용하기도 한다.

 

컬럼 모델 NoSQL로는 HBase, 카산드라, 하이퍼 테이블 등이 있다.

 

데이터 저장 방법

다음은 컬럼 모델 NoSQL의 데이터 저장 방식을 설명하는 그림이다.

로우키의 정렬은 바이트 단위의 사전식 정렬을 따른다. 위에 보이는 키의 순서와 같이 rowkey2보다 rowkey11이 더 앞에 위치한다. 또한 컬럼 패밀리에 포함된 컬럼의 정렬도 로우키의 정렬 순서와 동일한 기준을 따라서 저장된다.  위에 보이는 컬럼의 맨 아래에 있는 13자리 숫자값은 데이터 저장 시간인 타임스탬프값이다. 버전 스탬프라고도 한다.

 

첫번째 로우에는 동일한 이름을 가진 컬럼1이 두 개 저장되어 있는데, 동일한 컬럼에 대한 쓰기 요청이 두번 있었기 때문이다. 이때 컬럼1을 조회하면 가장 나중에 저장된 값2가 조회된다. 조회 옵션에 따라서 과거 버전의 값을 조회할 수 있다. 또한 저장 옵션에 따라서 몇 개의 버전을 남겨둘지도 지정할 수 있다.

 

데이터 변환

관계형 데이터베이스에 저장된 데이터를 컬럼 모델 NoSQL에서 어떻게 다루는지 알아본다. 

위와 같이 하나의 로우키에 한 명의 주문 정보를 저장하였으며, 스키마에 order와 location 컬럼 패밀리를 정의했다. 위 데이터에 사용자1의 새로운 주문을 생성하고 조회하는 예제를 알아본다. 사용자1은 원두커피를 구매했고 배송지는 강남구 신사동이며, 주문번호는 4라고 가정한다.

 (1) 주문번호가 포함된 주문 정보 컬럼을 생성하고 배송 정보 컬럼을 생성한다.

 (2) 사용자1의 주문 정보 로우인 user:1:orderlist 키에 생성된 주문 정보와 배송 정보 컬럼을 저장한다.

 

단 한번의 입력 연산으로 모든 처리를 완료했다. 심지어 동일한 주문 정보가 이미 있더라도 오류없이 실행된다. 이번에는 사용자1이 구매한 상품 목록을 조회해본다. 사용자의 구매 목록 조회를 위해서 주문 정보가 저장된 user:1:orderlist 키를 조회한다. 이후 조회된 전체 컬럼에 대하여 주문과 배송지 정보를 합친다. 이는 단 1회의 조회 연산으로 완료할 수 있다.

 

적절한 사용처

대부분의 컬럼 모델 NoSQL은 쓰기와 읽기 중에서 쓰기에 더 특화되어 있다. 쓰기 연산은 데이터를 먼저 커밋로그와 메모리에 저장한 후 응답하기 때문에 매우 빠른 응답 속도를 제공한다. 이와 같은 이유로 읽기 연산 대비 쓰기 연산이 많은 서비스나 빠른 시간 안에 대량의 데이터를 입력하고 조회하는 서비스를 구현할 때 가장 좋은 성능을 나타낸다. 예를 들어 채팅 내용 저장, 메일 저장소, 알림 내용 저장, 실시간 분석을 위한 데이터 저장소 등의 서비스 구현에 적합하다. 카산드라는 페이스북에서 메일과 쪽지의 알림을 처리하기 위하여 개발됐다. HBase는 대용량 데이터 분석에 주로 사용된다.

4. 그래프 모델 NoSQL

그래프 모델 NoSQL은 노드와 관계(또는 버텍스와 엣지라고 한다)를 사용하여 데이터를 저장하고 조회하는데, 관계는 속성이라는 부가 정보를 가진다. 관계형 데이터베이스는 엔티티와 관계를 레코드로 표현하는데, 그래프 모델 NoSQL은 노드와 관계로 표현한다. 예를 들어 사용자와 구매 정보를 그래프 모델 NoSQL에서 표현한다면, 사용자와 상품은 노드가 되며 구매는 관계가 된다. 그래프 데이터베이스는 관계형 데이터베이스와 가장 유사한 NoSQL이다.

 

공통 특징

그래프 모델 NoSQL은 노드와 노드 간의 관계를 저장하며 노드가 하나일 때는 관계를 지정할 수 없다. 노드의 조회를 위해서 관계를 조회 조건으로 사용한다. 관계는 방향성을 가지기도 하는데, 이 방향성에 의하여 여러 단계의 조회가 가능하다. 이를 순회라고 한다. 하나의 노드에서 다른 노드까지 도착하는데 여러 경로가 존재할 수 있다. 이로 인해서 데이터 조회를 위한 순회의 깊이를 제한하기도 한다. 그래프 모델 NoSQL은 많이 알려지지 않았기 때문에 서비스 도입 사례가 적으며, 그래프 자체의 복잡성으로 인하여 도입을 위한 기술적 장벽이 있다.

 

데이터 저장 방법

다음은 그래프 모델 NoSQL의 논리적 데이터 저장 방식을 나타내고 있다.

위와 같이 각 사용자와 영화가 노드로 표현되고 각 노드 간의 연결이 관계로 표현된다. 이를 관계형 데이터베이스의 구성요소와 대응시키면 엔티티 테이블은 노드, 관계 테이블은 노드 간의 관계가 된다. 그래프 데이터베이스의 장점은 관계를 통해 부가 정보를 조회할 수 있다는 데 있다. 

 

예를 들어 위 예제에서 송이에게 다른 친구를 추천하려면 내 친구의 친구를 조회하면 된다. 즉, 자신을 기준으로 2번의 순회 안에 들어 있는 친구 관계를 조회하여 추천할 수 있다. 순회 횟수를 늘리면 더 많은 친구를 조회할 수 있다. 조금 다른 정보를 찾아보자. 민선에게 영화를 추천하는 기능을 생각해보자. 민선의 친구는 송이와 태현인데 그 친구들은 영화를 관람하지 않았다. 이때 관계형 데이터베이스에서는 영화를 추천하기 위해서 다른 테이블의 정보를 조회하지만, 그래프 모델 NoSQL에서는 노드의 순회 횟수만 한번 증가시키면 된다.

 

데이터 변환

관계형 데이터베이스에 저장된 데이터를 그래프 모델 NoSQL에서 어떻게 다루는지 알아본다. 

위 예제에서 노드는 사용자 타입, 주문 타입, 상품 타입으로 이뤄졌다. 즉, 노드에 타입이 지정된다. 관계형 데이터베이스의 엔티티가 이름을 가지듯이 노드 또한 타입이라는 구분을 가진다. 사용자 노드에 지화자, 자린고비, 아끼자가 저장되어 있으며, 주문 노드에는 '배송지' 정보와 '상품과 구매상품 관계'가 저장되어 있다. 다른 노드와 관계를 가지지 않는 아끼자 노드도 저장된다. 위와 같이 각 노드는 노드의 타입을 가지고 있으며, 노드의 이름과 노드의 타입을 조합하여 데이터를 조회한다.

 

새로운 주문을 추가해본다. 사용자 지화자가 원두커피를 구매했고 배송지는 강남구 신사동이라고 가정한다.

  (1) 배송지 정보를 포함한 주문 타입의 노드를 생성하여 추가한다.

  (2) 사용자와 주문 노드 간의 구매 관계를 추가한다.

  (3) (1)에서 추가한 주문 노드와 구매한 상품 노드 간의 구매상품 관계를 추가한다.

 

1회의 노드 생성 연산과 2개의 관계 추가 연산으로 처리를 완료할 수 있다. 관계형 데이터베이스의 외래 키에 해당하는 관계를 그래프 모델 NoSQL에서는 노드 간의 관계로 표현한다고 생각하면 된다. 사용자 지화자가 구매한 상품의 목록을 조회하려면 지화자의 모든 구매 관계에 해당하는 노드와 주문 상품 관계에 해당하는 노드를 조회하면 된다. 즉, 2단계의 순회를 통하여 조회할 수 있다.

 

적절한 사용처

그래프 모델 NoSQL은 친구 추천과 같은 연관검색을 위한 정보를 저장하고 조회하는데 적합하다. 기존 관계형 데이터베이스에서 구현하기 까다로운 부분이 그래프 모델 NoSQL에서는 기본으로 제공되기 때문이다. 추천 시스템, 다중 관계를 가진 엔티티를 저장하고 조회하는 데 알맞다.

 

많은 그래프 모델 NoSQL은 각 데이터에 대한 관계를 저장하기 때문에 데이터를 다중 노드(저장소 노드)에 분산하여 저장하는 데 많은 한계가 있다. 예를 들어 지화자 노드와 주문1 노드가 분산되 저장소에 저장되어 있을 때 관계를 조회하면 원격지의 저장소에 접근해야 하기 때문에 성능이 떨어지게 된다. 이런 이유로 분산처리를 지원하는 그래프 모델 NoSQL은 많지 않다.


언제 NoSQL을 사용해야 하는가

관계형 데이터베이스는 충분히 성숙된 기술이며 범용 데이터 저장소로서의 역할을 충분히 수행하고 있다. 복잡한 업무 로직을 처리하는 분야에서도 관계형 데이터베이스가 더 나은 성능을 보인다. 그럼에도 불구하고 NoSQL이 필요한 상황이 발생할 수 있다. 문서 모델은 하나의 키에 하나의 구조화된 문서를 저장하고 조회한다. 

 

대량의 단순 정보를 빠르게 저장하고 조회할 때, 관계형 데이터베이스가 처리하지 못하는 대량의 데이터를 입력할때(보통 수십 기가바이트의 데이터), 스키마가 고정되지 않은 데이터를 저장하고 조회할 때 등이 이에 해당된다. 관계형 데이터베이스의 특성상 제공 가능한 성능에 한계가 있다. 그 한계를 넘어선 성능이 필요할 때 NoSQL을 선택하면 된다(수십 기가바이트의 데이터로 실시간 서비스를 하기 위해서는 데이터베이스의 하드웨어에 들어간 비용이 이미 수천만 원을 호가할 것이며, 나중에는 더 이상 하드웨어나 CPU나 메모리를 추가 증설할 슬롯 조차 남아있지 안을 거이다)

 

위와 같은 상황에 처해 NoSQL 도입을 확정했다면, 다음으로는 어떤 NoSQL을 사용해야 할지 결정해야 한다. 사실 '언제 NoSQL을 사용해야 하는가?'와 같은 질문보다 더 중요한 것은 '어떤 NoSQL을 선택해야 하는가?'다.

 

이 문제가 왜 중요한지 음악 스트리밍 서비스를 예로 들어 알아본다. 도입할 NoSQL은 카산드라로 정했으며 6대의 노드를 사용한다고 가정한다. 이 서비스에서는 사용자가 좋아하는 음악을 추천하기 위해서 사용자의 활동을 음원 클릭 현황이라는 테이블에 기록하고 있다. 음원 클릭 현황이라는 테이블은 사용자가 선택한 카테고리에 대한 클릭수와 각 음악의 클릭수를 저장하다. 이 테이블의 레코드는 매일 수천만 번의 변경이 발생하고 하루에 한번 조회된다.

 

음악 클릭 횟수를 저장하는 방법을 생각해본다. 먼저 사용자의 음악 클릭 횟수를 저장하는 컬럼을 조회한다. 조회된 값이 존재하면 기존 값에 1을 더하여 저장하고, 그렇지 않으면 값이 1인 컬럼을 저장한다. 이 구현이 정상적으로 동작할까? 이와 같은 구현방법은 잘못된 값이 저장될 가능성이다.

 

카산드라의 일관성 레벨과 변경 전파 이벤트에 대해서 잠시 생각해보자. 복제계수가 3으로 설정됐다고 가정하면, 논리적으로 3개의 다른 값을 가진 컬럼이 존재할 수 있다. 동시에 5명의 사용자가 동일한 MP3 파일을 클릭했다고 가정하면 최종 저장되는 값을 최소 1에서 최대 5가 될 수 있다. 물론 카산드라는 카운터라는 필드를 제공하며 99.9%의 정확도록 일관성을 제공할 수 있다. 하지만 이 값이 숫자가 아닌 문자라면? 완벽한 일관성을 제공받기 위해서는 카산드라에서 복제계수를 1로 설정하여 가용성을 포기하든지, 일관성 레벨을 All로 설정하여 성능을 희생해야 한다. 보통 이런 상황에서는 일관성 레벨을 All로 설정한다. 겨로가적으로 고나계형 데이터베이스와 비교하여 큰 차이 없는 성능을 얻게 되거나 때에 따라서는 더 느린 성능을 제공할 수도 있다.

 

이런 상황을 맞닥뜨리게 된다면 NoSQL 도입은 실패로 돌아가고 결국 'NoSQL은 쓸 게 못되는군'이라는 섣부른 결론을 내리게 된다. 이런 상황을 피할 방법은 없는걸까?

 

관계형 데이터베이스에서 NoSQL로 데이터 저장소를 변경할 때, 가장 먼저 고려해야 할 사항은 일관성 모델이다. 구현된 서비스에 강한 일관성 모델이 필요한지, 느슨한 일관성 모델을 사용해도 큰 문제가 되지 않는지에 대한 판단이 우선되어야 한다. 그렇지 않다면 위와 같은 상황을 만나게 된다. NoSQL을 선택할 때 고려할 사항은 다음과 같다.

 

일관성 모델

위의 예제에서 살펴본 바와 같이 제공하자고자 하는 서비스에서 어느 정도의 일관성이 필요한지 먼저 확인해야 한다. 강한 일관성이 필요한 서비스를 구현하기 위해서 궁극적 일관성을 지원하는 카산드라를 선택하는 것은 득보다 실이 클 수 있다. 일관성은 데이터 저장 모델과는 크게 상관이 없다.

 

데이터 모델

제공하려는 기능이 키-값 모델과 같은 간단한 데이터 모델로 처리가 가능한지 또는 문서 모델과 같이 중첩된 구조를 지원해야 하는지 판단해야 하는데, 이 부분은 실제 구현에 따라서 달라질 수 있다. 결론적으로, 선택한 NoSQL의 데이터 모델로 필요한 기능을 구현할 수 있는지에 대해 판단해야 한다.

 

읽기 쓰기 성능

제공할 기능의 읽기와 쓰기 비율에 따라서 선택할 NoSQL도 바뀌게 된다. 예를 들어 읽기 쓰기 모두에 빠른 응답시간이 필요하다면 인 메모리 NoSQL이 후보가 될 수 있으며, 상대적으로 읽기 비율이 높다면 B트리 인덱스 구조를 사용하는 문서 모델 NoSQL이 후보가 될 수 있다.

 

단일 고장점

선택한 NoSQL이 단일 고장점을 가지고 있는지 확인하여야 하며, 단일 고장점을 가지고 있더라도 쉬운 복구가 가능한지 확인해야 한다. 예를 들어 HBase는 단일 고장점을 가지고 있지만 하드웨어적인 방법을 통해서 단일 고장점을 제거할 수 있다. 또한 보조 네임 노드를 사용하고 있기 때문에 장애 상황에서 빠른 복구도 가능하다. 무정지 서비스가 중요 목표라면 단일 고장점을 가진 NoSQL 선택을 피해야 한다.

 

원자성 지원

선택한 NoSQL의 트랜잭션 지원 여부, 단일 연산에 대한 원자성 지원 여부와 같은 CAP 특징을 확인해야 한다. 원자성의 지원이 어느 쪽(서버, 클라이언트)에서 지원되는지 확인하여야 한다. 클라이언트에서 지원하는 단일 연산의 원자성을 코드의 복잡성을 증가시킬 수 있다.

 

하드웨어 구성

해당 NoSQL이 가지는 시스템 아키텍처를 확인해야 한다. 가용성을 지원하기 위해서 마스터-슬레이브 구조의 NoSQL을 선택했다면 저장되는 데이터의 최대 크기는 절대적인 저장소 크기의 절반이다. 또한 NoSQL 내부의 구성요소와 하드웨어에 대한 기본 구성 정보를 알아야 한다. 예를 들어 HBase는 최소 5대 이상의 하드웨어에서 수행되어야만 성능의 선형 증가를 얻을 수 있다.

 

무중단 시스템

시스템을 확장할때 시스템 중단이 필요한지 여부와 같은 시스템의 특성을 확인해야 한다. 예를 들면 MongoDB와 같이 자동 샤딩을 지원하는 NoSQL은 운영 중에 시스템을 추가할 수 있지만, 자동 샤딩 중에는 서비스 응답시간이 느려지기도 한다.

 

위와 같은 고려사항을 확인하여 서비스에서 필요한 부분과 필요없는 부분을 먼저 선택하고 난 뒤 그에 맞는 NoSQL을 선택해야 한다. 즉, 필요한 요구 사항을 모두 만족하는 NoSQL을 선택해야 한다. 일반적으로 NoSQL을 사용한 서비스 구현은 한 가지 NoSQL만을 사용하여 모든 서비스를 제공하도록 구성하지 않는다. 따라서 서비스를 부분으로 나누어 별도의 NoSQL을 배치하고 주 저장소는 관계형 데이터베이스를 사용하기도 한다.

 

만약 주 서비스에 NoSQL을 도입하는 것이 꺼려진다면 부분적인 도입으로부터 출발하는 것도 좋은 방법이다. 중요도가 낮거나 장애 시 영향도가 낮은 서비스를 대상으로 먼저 적용한다. 이러한 방법은 처음 NoSQL을 도입하는 시험 단계에서 많이 선택된다.


NoSQL에 대한 전체적인 개념과 CAP 이론, NoSQL의 분류에 대하여 살펴봤다. 또한 NoSQL에 대한 이해도를 높이고자 어떻게 실제 서비스에 응용할 수 있는지에 대한 정보를 제공하고, NoSQL에 대한 오해르 예방하기 위하여 알려진 NoSQL 솔루션들의 장단점을 알아보았다.

 

실제로 많은 사용자가 NoSQL에 대한 오해로 인하여 초기 도입 시에 많은 고통을 겪기도 한다. 주로 NoSQL의 장점만 보고 쉽게 도입을 결정하기 때문인데, 개발 초기에 수많은 시행착오와 생소한 저장 모델에 대한 설계 난이도의 장벽을 넘지 못하는 경우가 왕왕 발생한다. 또한 힘겹게 개발을 완료하여 실제 운영으로 들어간 이후에도 많은 장애 상황을 겪기도 한다.

 

기본 지식없이 무턱대고 NoSQL을 사용하여 서비스한다면, 안정화까지 매우 많은 시간과 노력이 들게 된다. 이번 장르 제대로 이해한다면 최소한 서비스에 맞지 않는 NoSQL을 선택하는 오류는 피할 수 있을 것이다.

 

자신의 서비스에 맞는 NoSQL을 선택할 때 비로소 NoSQL의 진가를 알수있게 된다. 많은 사용자들이 NoSQL을 자신의 서비스에 적용하려 한다. NoSQL 신봉자들은 NoSQL은 신기술이며 글로벌 서비스에서 적용된 사례도 충분하기에 다음 세대의 기술이라 생각한다. 또한 NoSQL을 적용하기만 하면 지금까지 겪었던 모든 문제들(스케일 아웃, 높은 가용성, 트래픽 처리를 위한 성능)이 한꺼번에 해결될 거라 믿는다.

 

하지만 NoSQL은 관계형 데이터베이스의 한계를 극복하고 모든 장점만을 모아둔 솔루션이 아니다. 기존의 관계형 데이터베이스가 제공하는 테이블 조인, 트랜잭션, SQL 문과 같은 편의성을 포기하고 스케일 아웃과 같은 NoSQL의 장점을 얻은 또다른 데이터베이스다. '얻는 것이 있으면 잃는 것도 있다'