앞의 포스팅에서 언급했듯이 NoSQL은 생겨난 이유는 비슷하지만 각각 접근 방법은 다르기 때문에 수많은 NoSQL들을 다양한 기준에 따라 분류할 수 있습니다.

CAP 이론
Consistent - Available : Postgres, MySQL같은 전통적인 RBMS
Consistent - Partition Tolerant : BitTable , Hypertable , HBase , MongoDB , Terrastore , Redis , Scalaris , MemcacheDB , BerkeleyDB
Available - Partition Tolerant : Amazon Dynamo , Cassandra , CouchDB , Amazon SimpleDB , Riak



분산형과 비분산형
분산형 : Amazon Dynamo, Amazon S3, Scalaris, Voldemort, CouchDB, Riak, MongoDB, BigTable, Cassandra, HyperTable, HBase
비분산형 : Redis, Tokyo Tyrant, MemcacheDB, Amazon SimpleDB

분산형 데이터베이스는 확장성을 위한 Data Partitioning과 Availability를 위한 복제에 대한 책임을 가지고 있고 비 분산형은 클라이언트가 이 책임들을 가지고 있습니다. 주목할만한 분산모델은 Dynamo에서 사용되었지만 Voldemort, Riak, Cassandra에서 이 모델을 카피해서 사용하고 있으며 이질적인 하드웨어간에도 효과적입니다.



데이터 모델의 풍부함(Richness)
Key-Value Store : Amazon Dynamo, Amazon S3, Redis, Scalaris, Voldemort
Document Store :
Amazon SimpleDB, Apache CouchDB, MongoDB, Riak
Column Store :
Scssandra, Google BigTable, HBase, Hypertable

이 분류에서 한쪽 끝은 Key-Value Store이고 반대쪽은 가장 부유한 Column Store입니다. 이 두 사이에는 관계형데이터베이스의 사촌정도가 되는 Document Store가 위치하고 있고 Key-Value보다는 풍부하고 이해하기가 쉽습니다.


Disk와 Memory
Memory : Scalaris, Redis
Configurable :
BigTable, Cassandra, HBase, HyperTable
Disk : CouchDB, MongoDB, Riak, Voldemort

Scalaris는 완전한 메모리 기반이고 Redis는 메모리주도적입니다. Configurable에 분류에 포함된 데이터베이스들은 많은 컨트롤을 제공하기 위해 얼마나 큰 메모리를 얻을 것인지를 설정할 수 있으며 Disk의 범주에 있는 CouchDB, MongoDB, Riak은 on-disk B+ Tree를 사용하며 Voldemort는 BDB와 MySQL을 사용하고 있습니다.






데이터 모델의 방식에 따라 주요한  NoSQL위주로 특징들을 정리했습니다.

Key-Value Store
Key-Value Store는 가장 간단하게 가능한 데이터 모델을 제공합니다. 범위쿼리는 데이터베이스에서 명백하게 지원하지 않는다면 쉽지 않습니다. 그리고 Key-Value Store상에서의 애플리케이션 모델링은 복잡성을 가질 수 있습니다.

Amazon Dynamo

  • 분산형 Key-Value Store로 값이 시스템에 불투명합니다. 타겟 오브젝트는 보통 1MB 미만으로 Amazon내부에서 사용되고 있고 외부에서는 직접 이용할 수 없습니다.
  • 일관성 해싱을 사용해서 Partitioning을 합니다.
  • 복제 - 데이터는 다중호스트에 복제됩니다.
  • 일관성 - 업데이트는 비동기적으로 복제는 전파하기 때문에 모델은 결국 일관성을 가집니다.



Amazon S3

  • 분산형 Key-Object Store로 오브젝트는 시스템에 불투명합니다. 각 오브젝트의 사이즈는 1byte ~ 5GB의 범위내에 있으며 오브젝트의 갯수는 제한이 없는 무제한 스토리지 입니다. 오브젝트는 Amazon 데이터센터 내에서 유지가 되며 오브젝트의 위치에 대한 GEO를 결정하며 저장되는 오브젝트가 크면 유용합니다.
  • 일관성 : 새로운 PUT에서 Read Your Write1를 이루고 PUT과 DELETE를 덮어써서 결과적인 일관성을 가집니다.





Document Store
Document Store는 Key-Value Store보다 한걸음 더 나아간 구조입니다. 스키마가 테이블의 형태보다 먼저 정의되어야 하는 관계형 데이터베이스와는 달라서 각 도큐먼트는 다른 스키마를 가질 수 있습니다. 그러면서도 관계형 데이터베이스처럼 레코드간의 관계를 설명하는 것이 가능합니다. 이 먼저 정의해야하는 스키마를 제외하고는 개념적으로 관계형 데이터베이스와 아주 유사합니다.

Amazon SimpleDB

  • Erlang으로 작성되었습니다.
  • 데이터는 관계된 데이터를 함께 보관하기 위한 컨테이너로써의 도메인과 도메인으로 연결하기 위한 엔티티, 아이템의 프로퍼티로써 속성과 그 값으로 모델되었습니다.
  • 기본적으로 각 아이템은 속성을 언제나 추가/삭제 가능한 Dictionary입니다.
  • 아이템이 추가될 때 속성에 기반하여 인덱스됩니다.
  • 결과적인 일관성을 갖습니다.
  • 파티셔닝 : 도메인 - 아이템 - 어트리뷰트 모델은 각 도메인의 사이즈와 도메딩당 속성의 갯수 및 도메인의 갯수에 대한 제한을 갖는다. 그러므로 확장을 위해서는 데이터를 수동으로 파티션해야 합니다.



Apache CouchDB

  • Erlang로 작성되었습니다.
  • 데이터와 스토리지 모델 :
    • 레코드는 JSON스트링(도큐먼트)의 형식으로 직렬화됩며 각 도큐먼트는 유니크한 DocId를 같습니다.
    • B+트리 를 사용하면 커밋된 데이터는 결코 덮어써지지 않고 오직 오퍼레이션만 추가합니다.
    • Append only 모델로 Read가 다중버전의 동시성 제어를 사용해서 수행됩니다.
      쓰기는 동시에 작성가능한 Blob외에는 직렬화됩니다.
    • 같은 데이터를 위한 다중 뷰를 가질 수 있습니다.
    • Design Document라고 부르는 도큐먼트의 특별한 타입에서 갑에 키를 매핑하는 Javascript함수를 사용하도록 정의되었습니다.
  • View함수는 View를 쿼리했을 때 실행되며 기본적으로 Map 함수와 Reduce 함수가 존재합니다.
  • 복제 : 다중마스터를 지원하는 마스터 슬레이브를 갖으며 오직 최신 리비전만 복제가 됩니다.
  • 멀티마스터에서의 충돌(Conflict) 핸들링 : 충돌을 탐지하고 winner를 선택하기 위해 결정론적인 알고리즘을 사용하며 여기서 winner는 기대한 것일수도 아닐 수도 있기 때문에 아닐 경우에는 애플리케이션에서 스스로 고쳐야 합니다.
  • Partitioning과 로드 밸런싱 - CouchDB Lounge 에 의해서 제공됩니다.
  • Apache 2.0 라이센스를 따릅니다.



MongoDB

  • C++로 작성되었습니다.
  • 데이터 모델
    • 컬렉션 - Amazon SimpleDB의 도메인과 유사하며 비슷한 도큐먼트들을 함께 두기 위한 Bucket입니다.
    • Key-Value는 바이너리 직렬화된 JSON(BSON - Binary Serialized JSON )입니다.
    • 한 BSON 객체는 4MB 제한이 있습니다.
    • 큰 객체는 GridFS 를 통해 지원하고 있습니다.
    • 인덱스를 위해서 B-Tree가 사용되며 필드를 파라미터로 받는 db.things.ensureIndex()함수를 사용해서 인덱스에서 필드를 열거합니다.
  • 스토리지
    • in-Place 업데이트입니다.
    • 부분적인 업데이트를 제공하기 때문에 전체 로우를 보내지 않고 값을 업데이트 할 수 있습니다.
    • Forward-only 커서를 지원합니다.
    • 단일 도큐먼트 원자적 업데이트를 지원합니다. 원자적인 배치 업데이트는 db.eval()을 통해서 가능하지만 파티션된 데이터는 지원안할 수도 있습니다.
  • 쿼리
    • 도큐먼트 내부에서 값을 선택하기 위한 Syntax기반의 JSON 스타일을 제공합니다.
    • 상황적인 Operator와 정규식 등을 지원합니다.
    • Forward-only 커서를 지원합니다.
    • 쿼리 옵티마이징은 다중 쿼리플랜을 시도하고 가장 잘 동작하는 것을 선택하는 형식입니다.
  • 배치 오퍼레이션 : 컬렉션에 Map-Reduce를 지원합니다.
  • 복제 : 마스터-슬레이브 보델로 자동적으로 마스터와 슬레이브의 역할을 이해하는 복제쌍(replica pair)입니다.
  • Partitioning
    • 알파스테이지에서 파티셔닝 됩니다.
    • 파편(Shards)는 Chunk라고 불리는 유닛에서 데이터를 다루며 Chunk는 최대 50MB의 컬렉션으루부터 인접하는 범위의 데이터입니다.
    • 데이터는 하나의 Shard에서 Chunk안의 다른 Shard로 마이그레이션 됩니다.
  • AGPL 3.0 라이센스를 따릅니다.





Column Store

BigTable

  • 구글에서 내부적으로 사용하면 Google App Engine를 통해서 공개됩니다.
    Block를 짓습니다.
    • Google File System 를 사용하여 분산 파일시스템과 Raw 스토리지를 제공합니다. 청크서버가 청크를 저장할 책임을 가지고 있으며 각 청크는 안전을 위해 3개의 머신에 복제가 됩니다. 데이터전송은 클라이언트와 청크서버 사이에서 직접 발생합니다.
    • 스토리지 파일 포맷은 정렬된 스트링 테이블로 SSTable로 불립니다.
    • 스케쥴러는 메선에서 스케쥴 잡으로 제공됩니다.
    • 분산된 Lock과 파일, 네임 매니저로 Chubby 서비스 를 사용한다.

  • 데이터 모델은 Map의 Map인 다차원 Map입니다. 데이터는 테이블로 조직화되고 테이블은 관계된 데이터를 가진 엔티티입니다.
  • Write : Write 요청이 타블렛 서버에 도달했을 때 잘 규격화 되었는지와 권한을 확인하며 유효한 변경은 커밋로그에 작성됩니다.
  • Read : Write요청과 비슷한 확인을 하며 SSTable의 순서와 Memtable의 합쳐진 View에서 실행됩니다.



Cassandra

  • BigTable의 컬럼패밀리(Column-family) 데이터 모델과 Dynamo의 분산 아키텍쳐를 합쳤습니다.
  • Java로 작성되었습니다.
  • 데이터 모델
    • BigTable같은 키에 의해 인덱스되는 다차원 맵을 사용하며 각 애클리케이션은 BigTable에서 테이블같은 Key-Space를 만듭니다. Key는 레코드를 위한 이름이고 Column은 레코드의 속성이며 타임스템프가 찍힙니다. Column-family는 컴럼의 그룹핑으로 관계형 테이블과 유사하고 Super-Column은 컬림들의 리스트입니다.
    • 소팅은 데이터의 쓰여진 시간으로 정렬되되며 컬럼들은 컬럼의 이름으로 Row내에서 정렬됩니다.
  • Partitioning : Dynamo와 유사하며 순서가 보장된 햄쉬함수를 사용하는 일관된 해시를 사용하고 Dynamo와는 다른 로드밸런싱을 위해소 Chord 접근을 사용합니다.
  • 복제 : Dynamo의 조정자 노트와 선호리스트의 건셉과 같으며 Datacenter aware Rack aware, Rack unaware같은 다양한 보제 정책들을 가지고 있습니다.
  • 퍼시스턴스 : 로컬파일 시스템에 의지하며 스토리지 구조와 접근방식은 BigTable과 유사합니다.
  • Write : Memtable에 대한 업데이트를 따르는 내구성과 복구를 위한 커밋로그를 쓰는 Bigtable의 접근과 유사하며 커밋로그를 위한 각 머신의 전용디스크는 Write를 순차적으로 만들어 쓰루풋을 최대화 합니다.
  • Read : 제공될 노드와 Read-repair를 찾는 Dynamo와 유사한 접근방식을 사용하며 스토리지 레벨에서의 접근은 BigTable과 유사한 Memtable + SSTable 시퀀스로부터 읽습니다.
    라이센스는 Apache 2를 따릅니다.
Footnote.
  1. Read-Your-Writes(RYW) 일관성은 레코드가 업데이트 되었을때 그 레코드에 대한 읽기시도는 업데이트된 값을 돌려주는 것을 보장해주는 것의 의미합니다. [Back]
2010/09/18 23:59 2010/09/18 23:59
크리에이티브 커먼즈 라이센스
Creative Commons License

'Database' 카테고리의 다른 글

DROP, DELETE, TRUNCATE의 차이점  (0) 2011.06.29
NoSQL에 대해서 #1  (0) 2011.06.09
웹이 점점 커지고 다양한 요구가 생겨나는 가운데 NoSQL이 커다란 이슈중에 하나로 떠오르고 있습니다. 제가 NoSQL에 대해서 처음 들은 것은 올 초정도로 기억하고 있습니다.

Google Trends에서 NoSQL로 검색한 결과

구글 트랜즈 에서 확인해보아도 NoSQL이라는 단어가 이슈화되기 시작한 것은 2009년 중순정도로 나타나고 있습니다.  위키피디아 에서 확인해 보면 NoSQL이라는 단어는 1998년 Carlo Strozzi이 SQL을 드러내지 않는 경량 데이터베이스로 이름지었고 2009년 초에 Last.fm 의 Johan Oskarsson이 오픈소스 분산데이터베이스에 대한 논의를 위한 이벤트를 원했을 때 Rackspace 의 직원인 Eric Evans가 NoSQL이라는 단어를 다시 소개했다고 합니다. 아틀란타에서 열린 no:sql conference 2009가 NoSQL논의에 큰 영향을 미쳤다고 합니다. (이 컨퍼런스의 모토인 "select fun, profit from real_world where relational=false;"가 상당히 센스넘치는군요)

올해 중에 NoSQL중 하나는 경험해 보자는 생각이었는데 마침 좀 만져볼 기회가 생겨서 좀 만져보고 있습니다. 개념자체가 개발자에게 기존에 익숙한 RDBMS와는 너무 달라서 튜토리얼 보고 단순한 사용정도는 할 수 있겠지만 관련해서 고민해 보려면 NoSQL에 대해서 좀 자세히 알아야 할 필요가 있게 느껴졌습니다. 사실 NoSQL에 대한 지식이 많다보니 고민 자체가 해결책이나 진도나 나가지 않고 계속 빙글빙글 도는 느낌이라 여기저기 자료를 좀 찾아서 정리해 보았습니다.





CAP Theorem
NoSQL에 대해서 이해하려면 먼저 CAP 이론에 대해서 알 필요가 있습니다. CAP이론은 Brewer's CAP Theorem 으로 알려져 있는데 분산 컴퓨팅 시스템에서 보장해야 하는 특징으로 아래 3가지를 정의하고 있습니다.

  • Consistency (일관성) : 모든 노드들은 동시에 같은 데이터를 보아야 합니다.
  • Availability (유효성) : 모든 노드는 항상 읽기와 쓰기를 할 수 있어야 합니다.
  • Partition Tolerance (파티션 허용차) : 시스템은 물리적인 네트워크 파티션을 넘어서도 잘 동작하여야 합니다

CAP 이론에 따르면 위 3가지 중에 동시에 2가지만 보장할수 있고 3개를 모두 보장하는 것이 불가능하다고 나와있습니다. 그래서 데이터를 관리할때 이 3가지 중에 어느 2가지에 중점을 두냐는 것은 아주 중요한 부분입니다. 이 부분을 이해하는데 Nathan Hurst 이 만든 아래의  Visual Guide to NoSQL Systems 는 큰 도움이 됩니다.

사용자 삽입 이미지

기존에 많이 사용하던 RDBMS는 3가지 중 CA에 집중하고 있습니다. 웹이 발전하면서 다양한 요구사항이 생겨나고 엄청난 양의 데이터를 처리해야 하게 되면서 RDBMS가 갖지 못한 P의 특성이 필요해졌고 그러면서 등장한 것이 NoSQL입니다. 좀더 풀어쓰면 데이터베이스에 대한 수평적 확장(Horizontal Scalability 즉 옆에 서버한대 더 배치해서 데이터베이스를 늘리고 싶다는 의미입니다.)에 대한 이슈가 발생했고 확장성이슈를 해결하기 위해서 P를 선택하다 보니 기존에 가지고 있던 C나 A의 특성중 하나를 포기해야 했습니다. 그래서 NoSQL에는 다양한 시도들이 있지만 가장 중요한 이슈는 확장성을 해결하려는 것으로 생각됩니다.

관계형 데이터베이스는 기본적으로 분산형을 고려해서 디자인 되지 않았습니다. 그래서 ACID(원자성, 일관성, 독립성, 지속성) 트랜잭션 같은 추상화와 고레벨 쿼리모델을 풍부하게 제공할 수 있지만 확장성이 좋지 못하기 때문에 모든 NoSQL 데이터베이스는 다양한 방법으로 확장성 이슈를 해결하기 위해 초점을 맞추고 있습니다. 각 NoSQL에는 여러가지 차이점들이 있지만 CAP의 범주에서만 보면 CP를 선택하거나 AP를 선택하게 됩니다.




왜 비관계형이어야 하는가?
NoSQL이 확장성 이슈를 해결하려고 CP나 AP의 특성을 선택했지만 구체적으로 어떤 특징을 선택하고 왜 그래야 했는지 이해할 필요가 있습니다. NoSQL은 많은 제품군들이 있는데 모두 같은 전략으로 접근하고 있지는 않고 각각에 제품에 따라 다양한 접근을 하고 있는데 아래 적힌 내용들은 비관계형으로 가기 위한 여러가지 특성들에 대한 이야기이고 제품군에 따라 아래의 특성들을 선택한 여부는 다른 것으로 보입니다. 아래의 내용은 상당부분 VINEET GUPTA가 작성한 NoSql Databases - Part 1 - Landscape 를 참고하였습니다. 잘 정리된 문서라서 참고하시면 도움이 될 것입니다.



관계형 데이터 베이스는 확장하기가 어렵습니다.

Replication - 복제에 의한 확장
Master-Slave 구조에서는 결과를 슬레이브의 갯수만큼 복제해야 하는데 N개의 슬레이브에서 읽을 수 있기 때문에 Read는 빠르지만 Write에서는 병목현상이 발생하게 기 때문에 확장성에 대한 제한을 가지게 됩니다.
다중 마스터구조에서는 마스터를 추가함으로써 쓰기의 성능을 향상시킬 수 있는데 대신에 충돌이 발생할 가능성이 생기게 됩니다.

Partitioning(Sharding) - 분할에 의한 확장
Read만큼 Write도 확장할 수 있지만 애플리케이션레이어에서 파티션된 것을 인지하고 있어야 합니다. RDBMS의 가치는 관계에 있다고 할 수 있는데 파티션을 하면 이 관계가 깨져버리고 각 파티션된 조각간에 조인을 할 수 없기 때문에 관계에 대한 부분은 애플리케이션 레이어에서 책임져야 합니다. 일반적으로  RDBMS에서 수동  Sharding 은 쉽지 않습니다.



필요없는 특성들

UPDATE와 DELETE
Update와 Delete는 전통적으로 정보의 손실이 발생하기 때문에 잘 사용되지 않으며 후에 데이터 검사 및 재활성화를 위해서 기록해둘 필요가 있습니다. 그리고 사용자가 커뮤니티를 탈퇴한다고 그들의 글을 지우지 않듯이 도메인 관점에서는 실제로 삭제되지 않습니다.  이런 접근을 하게 되면 Update / Delete를 모두 Insert로 모델할수 있고 과거 데이터는 버전을 붙혀서 기록할 수 있으며 이 데이터들은 비활성데이터들이 됩니다. 이 INSET-only 시스템에서는 2개의 문제가 있는데 데이터베이스에서 종속(cascade)에 대한 트리거를 이용할 수 없으며 Query가 비활성 데이터를 걸러내야 할 필요가 있습니다.

JOIN
데이터가 많을 때 JOIN은 많은 양의 데이터에 복잡한 연산을 수행해야 하기 때문에 비용이 많이 들며 파티션을 넘어서는 동작되지 않기 때문에 피해야 합니다. 정규화는 일관된 데이터를 가지기 쉽게 하고 스토리지의 양을 줄이기 위해서 하는건데 반정규화(De-normalization)를 하면 JOIN문제를 피할 수 있습니다. 반정규화로 일관성에 대한 책임을 디비에서 애플리케이션으로 이동시킬수 있는데 이는 INSERT-only라면 어렵지 않습니다.

ACID 트랜젝션
Atomic (원자성) : 여러 레코드를 수정할 때 원자성은 필요없으며 단일키 원자성이면 충분합니다.
Consistency (일관성) : 대부분의 시스템은 C보다는 P나 A를 필요로 하기 때문에 엄격한 일관성을 가질 필요는 없고 대신 결과의 일관성(Eventually Consistent )을 가질 수 있습니다.
Isolation (격리성) : 읽기에 최선을 다하는(Read-Committe) 것 이상의 격리성은 필요하지 않으며 단일키 원자성이 더 쉽습니다.
Durability (지속성) : 각 노드가 실패했을때도 이용되기 위해서는 메모리가 데이터를 충분히 보관할 수 있을정도로 저렴해지는 시점까지는 지속성이 필요합니다.

고정된 스키마
RDBMS에서는 데이터를 사용하기 전에 스키마를 정의해야하고 Index등을 정의해야 하는데 현재의 웹환경에서는 빠르게 새로운 피쳐를 추가하고 이미 존재하는 피쳐를 조정하기 위해서는 스키마 수정이 필수적으로 요구됩니다. 하지만 컬럼의 추가/수정/삭제는 row에 lock을 걸고 index의 수정은 테이블에 락을 걸기 때문에 스키마 수정이 어렵습니다.



어떤 특성들은 갖지 않습니다.
계층화 데이터나 그래프를 모델하는 것은 어렵습니다. 또한 빠른 응답을 위해서 디스크를 피하고 메인 메모리에서 데이터를 제공하는 것이 바람직한데 대부분의 관계형 데이터베이스는 디스크기반이기 때문에 쿼리들이 디스크에서 수행됩니다.





기대하는 특성들
NoSQL이 바라는 환경은 서버들이 다른 용량들을 가지고 수업이 퍼져나가는 것으로 이를 노드라고 부릅니다.

높은 확장성
점진적으로 노드를 추가할 수 있어야 하고 이는 파티셔닝을 통해서 가능합니다.

높은 Availability
실패의 단일포인트가 없으며 데이터는 복제되기 때문에 어떤 노드가 죽었을때도 데이터는 이용이 가능합니다.

높은 성능
디스크대신 메모리 기반으로 결과는 빠르게 리턴되어야 하며 이는 논블락킹 Write와 낮은 복잡성을 가진 알고리즘을 통해서 이룰수 있습니다.

원자성
각각의 쓰기는 원자성을 가질 필요가 있다.

일관성
강한 일관정은 필요없고 결과적인 일관성만 가지면 된다.(Read-Your-Writes1 일관성)

지속성
데이터는 휘말성 메모리만이 아닌 디스크에서 유지되어야 합니다.

배포의 유연함(Flexibility)
노드의 추가/삭제는 데이터를 분산하고 수동으로 중재할 필요없이 자동적으로 로드되어야 하며 분산 파일 시스템이나 공유스토리지 요구같은 제약이나 특수한 하드웨어같은 것이 필요없어야 합니다. 이기종간의 하드웨어에서 동작가능해야 합니다.

모델링의 유연함(Flexibility)
Key-Value쌍, 계층형 데이터, 그래프등 여러가지 타입의 데이터를 간단하게 모델할 수 있어야 합니다.

쿼리의 유연함(Flexibility)
하나의 호출에서 제공된 키에 대한 값이 묶음을 얻는 다중 GET과 키의 특정 범위에 기반한 데이터를 얻는 범위 쿼리가 필요합니다.


이 포스팅은 NoSQL에 대해서 #2로 이어집니다.
Footnote.
  1. Read-Your-Writes(RYW) 일관성은 레코드가 업데이트 되었을때 그 레코드에 대한 읽기시도는 업데이트된 값을 돌려주는 것을 보장해주는 것의 의미합니다. [Back]
2010/09/18 23:57 2010/09/18 23:57
크리에이티브 커먼즈 라이센스
Creative Commons License

'Database' 카테고리의 다른 글

DROP, DELETE, TRUNCATE의 차이점  (0) 2011.06.29
NoSQL에 대해서 #2  (0) 2011.06.09

+ Recent posts