카산드라
카산드라(Cassandra)는 대용량 데이터 처리가 가능한 비관계형 분산 데이터베이스 관리 시스템(DBMS)이다. 빅데이터 처리를 위한 노에스큐엘(NoSQL)의 일종이다. 페이스북(Facebook)이 개발했고 오픈소스로 공개했다. 정식 명칭은 아파치 카산드라(Apache Cassandra)이고, 아파치재단에서 관리하고 있다.
개요
아파치 카산드라(Apache Cassandra)는 자유-오픈 소스 분산형 노에스큐엘(NoSQL) 데이터베이스 관리 시스템(DBMS)의 하나로, 단일 장애점 없이 고성능을 제공하면서 수많은 서버 간의 대용량의 데이터를 관리하기 위해 설계되었다. 카산드라는 여러 데이터센터에 걸쳐 클러스터를 지원하며 마스터리스(masterless) 비동기 레플리케이션을 통해 모든 클라이언트에 대한 낮은 레이턴시 운영을 허용한다. 카산드라는 또한 성능 면에서 높은 가치를 보인다. 2012년, NoSQL 시스템을 연구하는 토론토 대학교 연구원들은 "확장성 면에서 실험 가운데 분명한 승자가 있다. 카산드라는 모든 실험의 최대 노드 수에서 가장 높은 처리량을 성취한다."고 결론을 내렸으며 그럼에도 불구하고 "높은 쓰기 및 읽기 레이턴시의 대가가 있다"고 이야기하였다.[1]
등장배경
카산드라는 아마존의 다이나모DB(DynamoDB) 설계에 참여한 아비나쉬 락슈만(Avinash Lakshman)과 페이스북의 프라샨트 말릭(Prashant Malik)이 최초로 만들었다. 아마존 다이나모DB의 분산 디자인과 구글 빅테이블의 데이터 모델을 기반으로 설계되었다. 2008년 카산드라는 페이스북에서 떠나 아파치 인큐베이터 프로젝트로 출발했으며, 2010년에 이르러 탑 레벨 아파치 프로젝트로 인정받는다.[1]
특징
카산드라(Cassandra)는 확장성(scalability)과 고가용성(high availability)에 최적화된 대표적인 분산형 데이터 저장소(Data storage)이다. 일관된 해싱(Consistent hashing)을 이용한 링(Ring) 구조와 가십 프로토콜(Gossip protocol)을 구현하였으며, 때문에 각 노드 장비들의 추가, 제거 등이 자유롭고, 데이터센터까지 고려 할 수 있는 데이터 복제 정책을 사용하여 안정성 측면에서 많은 장점을 가지고 있다. Cassandra를 이용하면 쉐어링(Sharding)을 고려해야 할 필요도 없고 마스터 슬래이브(Master Slave) 와 같은 정책이 없이도 장애에 대응할 수 있으며, 필요에 따라 장비들을 늘리고 줄이는 데 큰 비용이 들지 않는다. 물론 그렇다고 해서 Cassandra가 완벽한 솔루션은 아니다. 당연한 이야기지만 모든 일에는 균형(trade off)이 있듯이 저러한 강력한 기능들이 구현됨으로써 반대로 수많은 단점들 역시 발생한다. Cassandra는 연결(Join)이나 거래(Transaction)를 지원하지 않고, 인덱스(Index) 등의 검색을 위한 기능도 매우 단출하다. 게다가 Cassandra의 구조상 관계형 데이터베이스 관리 시스템(RDBMS)와 같은 Paging을 구현하는 것이 힘들고 키스페이스(Keyspace)(RDBMS의 DB와 같은)나 테이블(Table) 등을 과도하게 생성할 경우 메모리 오버플로어(Memory Overflow)가 발생할 수 있음을 고려하여야 한다. 따라서 Cassandra가 기존의 RDBMS들의 완벽한 대체품이라고 할 수는 없으므로, 개발하게 될 제품의 기능과 특징에 따라 Cassandra를 사용할 것인지 RDBMS를 사용할 것인지를 신중히 결정하여야 한다.[2]
데이터 구조
카산드라(Cassandra)의 데이터 구조는 비교적 간단하다. 최상위에 논리적 데이터(Data) 저장소인 키스페이스(Keyspace)가 있고, Keyspace 아래에는 테이블(Table)이 존재한다. Table은 다수의 로우(Row)들로 구성되어있으며 각 Row는 키값(KeyValue)으로 이루어진 칼럼(Column)들로 구성된다. 관계 데이터베이스 관리 시스템(RDBMS)의 데이터베이스 테이블 로우 칼럼(DB-Table-Row-Column)의 형태와 유사한 구조를 하고 있다는 걸 알 수 있다. 더구나 Cassandra는 현재 CQL (Cassandra Query Language)을 지원하고 있다.[2]
카산드라(Cassandra)는 기본적으로 링(Ring) 구조를 띠고 있다. 그리고 Ring을 구성하는 각 노드에 데이터(Data)를 분산하여 저장한다. 파티션키(Partition Key)라고 불리는 데이터의 해쉬(hash)값을 기준으로 Data를 분산하게 된다. 처음 각 노드가 Ring에 참여하게 되면, Cassandra의 conf/cassandra.yaml에 정의된 각 설정을 통하여 각 노드마다 고유의 hash 값 범위를 부여 받는다. 그런 뒤에, 외부에서 data의 요청(request)이 오게 되면 해당 데이터의 partition key(Row key)의 hash 값을 계산하여 해당 데이터가 어느 노드에 저장되어 있는지 알고 접근할 수 있는 것이다. 그리고 Cassandra는 이렇게 계산된 hash의 값을 토큰(token)이라고 부른다.현재 Cassandra는 CQL(Cassandra Query Language)의 사용을 권장하고 있지만 CQL이 처음부터 제공되었던 것은 아니다.[2]
초기의 Cassandra는 절약 프로토콜(Thrift protocol)을 이용한 클라이언트 에이피아이(client API)를 제공하였고, 아예 Cassandra에 직접 접근해보고 싶다면 bin/cassandra-cli 유틸리티를 이용하여 데이터를 확인할 수 있다. 이러한 기존의 절약(Thrift) 기반의 에피아이(api)와 씨엘아이(cli) 유틸리티들은 카산드라 데이터 구조(Cassandra Data layer)를 있는 그대로 표현해주었지만, Thrift가 가지는 여러 가지 한계점을 같이 가지고 있다. 때문에 Cassandra 1.2버전 이후에는 기본 프로토콜(Native Protocol)을 기반으로 한 API와 CQL 문법이 추가되었고, 3.0 버전부터는 기존의 Thrift 기반의 bin/cassandra-cli 유틸리티는 아예 파괴(Deprecated) 되어 사라졌다. 더구나 이 시기에 Cassandra에는 다른 많은 변화가 같이 있었다. 슈퍼 칼럼(Super Column)이라고 하는 Column 안에 Column 형태를 가지는 자료구조가 아예 스펙에서 제외되어 수집(Collection)이라는 것으로 새롭게 대체되었으며, 기존의 칼럼 구성(Column Family)이라고 불리는 자료구조는 테이블(Table)로 명칭이 변경되었다. 이 과정에서 CQL은 이렇게 새롭게 구성된 Cassandra Data Layer를 추상적으로 표현하는 문법으로 구성되었고, 따라서 실제 데이터 구조(Data Layer)의 용어들과 CQL에서의 표현이 1:1로 매칭되지 않고 달라지게 되었던 것이다.[2]
초창기 카산드라 데이터 구조(Cassandra Data Structure)는 키스페이스(Keyspace) > 칼럼구성(Column Family) > Row > Column 형태로 구성되어 있다. Keyspace와 Column Family에 대한 정보는 모든 카산드라 절(Cassandra node)의 메모리(memory)에 저장되며, 실제 유저의 데이터들이 저장되는 Row는 각 Row key를 가지고 이것의 hash 값인 token을 기준으로 각 노드에 분산 저장된다. 그리고 Row에 속하는 Column들은 칼럼 이름(Column name)을 기준으로 정렬되어 저장된다. 이러한 형태는 Cassandra 1.2 에 들어서 Keyspace > Table > Row > Column 로 명칭이 바뀌게 된다. 하지만 이때 함께 등장한 CQL(Cassandra Query Language)은 이를 있는 그대로 표현하지 않고, 한 단계 추상화하여 표현한다.[2]
여전히 같은 의미로 사용되는 키스페이스(Keyspace)와 테이블(Table)과는 다르게, CQL(Cassandra Query Language)에서의 Row와 Column은 실제 데이터가 저장되는 지금까지 보았던 카산드라 데이터 구조(Cassandra Data Layer)에서의 Row, Column과 그 의미가 다르다. 그림에서 알 수 있듯, CQL에서 Row와 Column은 관계 데이터베이스 관리 시스템(RDBMS)의 뜻,속성(Tuple, Attribute)과 유사하다는 것을 알 수 있다. 하지만 이렇게 구성된 CQL Table은 최소 1개 이상의 Column을 주요키(primary key)라는 것으로 지정해야 하며, Cassandra는 이렇게 primary key로 지정된 column들 중에서 부분키(partition key)로 지정된 column의 가치(value)를 기준으로 데이터를 분산하게 된다.[2]
CQL(Cassandra Query Language) key
카산드라(Cassandra)의 주요 키(Primary key)는 파티션 키(Partition Key)와 클러스터 키(Cluster Key)로 구성되어 있다. 주요 키 설정은 테이블을 생성할 때 적용할 수 있다. 관계 데이터베이스 관리 시스템(RDBMS)의 쿼리문과 비슷하게 사용할 수 있다. 파티션키는 필수적으로 설정되어야하고, 복합키로도 설정이 가능하다. 카산드라의 데이터는 파티션키를 기준으로 저장되기 때문에, 파티션의 크기, 파티션 내 데이터 순서, 클러스터 노드 간 파티션 분포를 고려하여 최소 1개 이상의 파티션 키를 가져야한다. 클러스터링 키는 파티션키 내부에서 데이터 순서를 정렬한다. 이 값은 필수적으로 설정하지 않아도 된다. 하지만 데이터를 최적화된 성능으로 가져오기 위해 데이터 구조에 맞는 클러스터 키를 설정한다. 복합 키로 설정할 땐 주요 키를 활용하여 괄호로 구분하면 된다.[3]
- 용어 정리
- 파티션 키(Partition key) : CQL(Cassandra Query Language)문법에서 카산드라에 데이터를 분산 저장하기 위한 유니크(unique)한 키(key)이다. 파티션 키는 특정 테이블(table)을 구성할 때 반드시 1개 이상이 지정되어야 하며, 여러개 지정될 수 도 있다. 파티션키가 단 1개일 경우, 해당 파티션 키로 지정된 CQL 칼럼(Column)의 가치가 실제 카산드라 데이터 구조의 로우 키(low key)로 지정된다. 파티션 키가 여러 개 일 경우, 각 파티션 키로 지정된 CQL 칼럼들의 가치들을 문자와 함께 조합한 값들이 실제 카산드라 데이터 구조의 로우 키로 저장된다.[4]
- 클러스터 키(Cluster key) : 카산드라 데이터 구조(Cassandra Data Layer)에서 로우에 속한 모든 칼럼(column)들은 항상 정렬된 상태로 저장된다. 따라서 클러스터 키는 이러한 정렬에 대한 기준 역할을 담당한다. CQL에서 클러스터 키로 지정된 CQL 칼럼들의 가치는 나머지 칼럼들의 이름 및 문자와 함께 조합되어, 이 값이 실제 카산드라 데이터 구조의 칼럼이름으로 저장된다. 만약 클러스터 키가 전혀 없는 경우에는 CQL 칼럼의 이름이 그대로 카산드라 데이터 구조의 칼럼 이름이 된다.[4]
- 주요 키(Primary key) : CQL 테이블(Cassandra Query Language table)에서의 각 로우(row)를 각자 유니크(unique)하게 결정해주는 기준 역할을 담당한다. 주요 키는 최소 1개이상의 파티션 키(partition key)와 0개 이상의 클러스터 키(cluster key)로 구성된다.[4]
- 복합 키(composite key) : 1개 이상의 CQL 칼럼(Cassandra Query Language Column)들로 이루어진 주요키(primary key)이다.[4]
- 복합 파티션 키(composite partition key) : 2개 이상의 다수의 CQL 칼럼(Cassandra Query Language Column)으로 이루어진 파티션 키(partition key)를 의미한다.[4]
- 칼럼(Column) : 카산드라 데이터 모델의 데이터 구조에서 가장 기본이 되는 단위다. 컬럼은 이름, 값, 클록(clock) 이라는 트리플릿(triplet)으로 구성된다. 클록은 현재 시간에 대한 타임스탬프라 할 수 있다. 관계형 분야의 "컬럼"에 익숙하겠지만, 카산드라에서도 이렇게 생각하면 헷갈리기만 한다. 무엇보다도 관계형 데이터베이스의 설계는 테이블의 모든 컬럼에 이름을 지정하고 테이블의 구조를 성계하는 방식을 따른다. 또한, 테이터를 쓸 때는 미리 정의한 구조에 값을 넣기만 한다. 그러나 카산드라에서는 컬럼을 미리 정의하지 않아도 된다. 다시 말해서 키스페이스에 원하는 컬럼 패밀리만 정의하고, 어딘가에 컬럼을 정의하지 않아도 데이터 쓰기를 시작할 수 있다. 이런 이유 때문에 카산드라는 모든 컬럼의 이름을 클라이언트에서 받는다. 컬럼 패밀리만 정의하면 되기 때문에 애플리케이션에서 데이터를 다루는 방법에 융통성을 발휘할 수 있으며, 시간의 경과에 따라 유기적으로 진화하는 설계가 가능하다. 이름과 값에 쓰이는 데이터 타입은 자바의 바이트 배열이며 종종 문자열로 제공되기도 한다. 이름과 값이 바이너리 타입이므로 길이에 제한이 없다. 클록의 데이터 타입 0.7 버전에서는 하위 호환성을 위해 타임스탬프가 유지될 것이다.[5]
- 클러스터(Cluster) : 단일 노드에서만 실행할거라면 카산드라가 최선의 선택이 아닐것이다. 카산드라 데이터베이스는 다중 머신에 분산되어 운영되지만 최종 사용자에게는 단일 인스턴스처럼 보이도록 설계되었다. 그래서 카산드라의 가장 바깥쪽 구조가 클러스터 이며, 이를 링(ring)이라 부르기도 한다. 이는 카산드라가 링에 데이터를 배열하고 그 데이터를 클러스터의 노드에 할당하기 때문이다. 노드는 범위가 다른 데이터의 복제본(replica)을 유지한다. 첫 번째 노드가 다운되면 복제본이 쿼리에 응답할 수 있다.[5]
- 키 스페이스(Key space) : 클러스터는 키스페이스의 컨테이너다. 키스페이스는 카산드라에서 데이터 컨테이너의 최외곽 컨테이너로 관계형 데이터베이스와 매우 비슷하다. 관계형 데이터베이스처럼 키스페이스는 이름과 키스페이스 전체 행위를 정의하는 속성 집합(set)을 갖고 있다. 애플리케이션마다 단일 키스페이스를 생성하는게 좋은 생각이라고 조언하지만, 이에 대한 실질적인 근거가 많지는 않다. 분명히 수용할 수 있는 방법이지만, 애플리케이션에서 필요한 만큼의 키스페이스를 생성하는 것이 좋다.[5]
- 설정 방법
- 칼럼(Column)에 직접 설정
CREATE TABLE emp( emp_id int PRIMARY KEV, emp_name text, emp_city text, emp_sal varint, emp_phone varint, );
- 주요 키(Primary key)로 설정
CREATE TABLE emp2( emp_id int, emp_name text, emp_city text, emp_sal varint, emp_phone varint, primary key (emp_id, emp_city) );
- 복합 키(composite key) 설정(파티션 키 : emp_id, emp_name, 클러스터링 키 : emp_city)
CREATE TABLE emp2( emp_id int, emp_name text, emp_city text, emp_sal varint, emp_phone varint, primary key ((emp_id, emp_name), emp_city) );
- 복합 키(composite key) 설정(파티션 키 : emp_id, 클러스터링 키 : emp_sal, emp_city)
CREATE TABLE emp2( emp_id int, emp_name text, emp_city text, emp_sal varint, emp_phone varint, primary key (emp_id, ( emp_sal, emp_city)) );
- 복합 키(composite key) 설정(파티션 키 : emp_id, emp_name, 클러스터링 키 : X)
CREATE TABLE emp3( emp_id int, emp_name text, emp_city text, emp_sal varint, emp_phone varint, primary key ((emp_id, emp_city)) );
- 복합 키(composite key) 설정(클러스터링 키 구체적인 옵션 부여)
CREATE TABLE emp5( emp_id int, emp_name text, emp_city text, emp_sal varint, emp_phone varint, primary key (emp_id, emp_city) )WITH CLUSTERING ORDER BY (emp_city DESC);
- 파티셔너(partitioner)
로우 키(Row Key)를 토큰(token)으로 변환해주는 모듈이다. conf/cassandra.yaml의 파티셔너(Partitioner)항목을 보면 카산드라(Cassandra)가 어떤 partitioner을 사용했는지 확인 할 수 있으며 RandomPartitioner(RP), Murmur3Partitioner(M3P), ByteOrderedPartitioner(BOP)라는 이름의 세 가지 Partitioner를 제공한다. RandomPartitioner는 Row Key를 MD5(Message Digest 5)로 해싱(hashing)하여 토큰(token)을 생성한다. Murmur3Partitioner은 MurMur5로 해싱하여 token을 생성한다. 하지만 ByteOrderedPartitioner는 조금 다르다. BOP는 Row Key를 16진수 형태로 변환하여 이 값을 token으로 사용한다. 즉,BOP 변환된 token은 문자 순서로 정렬되어 각 노드에 분산된다는 이야기다. 만약 BOP(ByteOrderedPartitioner)를 쓴다면 Row Key들이 문자순서로 정렬되어 있으니 대용량 데이터를 특별한 가공 없이 그대로 범위 질의(range query)를 할 수 있는 등, 여러가지로 편리하게 사용할 수 있다. 하지만 BOP는 카산드라(Cassandra)의 대표정 안티패턴 중 하나이다. BOP를 사용할 경우 핫스팟(Hotspot)이 발생 할 확률이 매우 높다.[4]
BOP를 사용하여 로우키(Row Key)문자 순서대로 각 노드 별로 데이터를 분산하게 된다면, 모든 노드에 데이터를 균일하게 분산하기 위해서는 데이터의 분산기준을 담당하는 Row key자체가 모든 문자열에 대애허 균일하게 분포해야 한다. 하지만 현실은 그렇지 않다. 특정 문자로 밀집되어 있는 Row key들을 저장하는 노드가 자연스럽게 Hotspot이 되어버린다. 노드를 늘렸더니 오히려 전혀 사용하지 않는 노드가 다수 생길 수도 있고, 노드를 줄였더니 더 심각한 Hotspot이 생겨버릴 수도 있기 때문이다. 이는 BOP의 경우 분산의 정도가 전적으로 Row key의 분포에 달려있기 때문이다. 이러한 이유로 디폴트는 Murmur3Partitioner을 사용한다. 이것을 사용함으로 모든 데이터를 비교적 균일하게 모든 노드에 분산 할 수 있는 것이다. M3 해시 함수(Murmur3 hash function)를 사용할 때 단점도 존재한다. 문자열이 아닌 해시(Hash)값을 기준으로 정렬하여 각 노드에 저장되므로 , Row key의 문자열로 정렬된 데이터가 필요 할 경우엔 사용자는 모든 데이터를 가져온 다음에 애플리케이션 구조(Application Layer)에서 직접 데이터를 가공하여 정렬하여야 한다. 가령 엄청난 양의 데이터가 분산되어 저장되어 있는데, Row key를 기준으로 데이터를 패깅(paging)하는 등의 작업은 불가능한 일이다. 그 많은 데이터를 모두 가져와서 직접 정렬해야 최종적으로 사용자가 원하는 위치의 데이터 집합을 뽑아 낼 수 있다.[4]
- 데이터 일관성과 복제
카산드라(Cassandra)는 기본적으로 CQL(Cassandra Query Language)을 통해 쿼리 시점에 읽기와 쓰기에 따른 다양한 일관성 수준(Consistency Level)을 통해서 몇 개의 복제(Replication)를 통해 어느 정도 수준의 데이터 일관성을 확보 할 것인지 선택 할 수 있다. 또한, 처음 키 스페이스(Keyspace)를 생성 할 때 복제(Replication)의 배치 전략과 그 전략에 맞는 복제 개수, 위치 위치를 결정 할 수 있다. 그리고 이러한 기능을 지원하기 위해서 conf/cassandra.yaml의 endpoin_snitch 항목에 스니치(snitch)의 종류를 세팅하게 된다. 스니치란 (데이터센터가 어떻게 구성되어있는지, 장비가 설치된 렉이 어떻게 나뉘어져 있는지에 대한 체계적인 분류(topology)를 Cassandra에게 알려주기 위한 옵션이다. Cassandra에서 제공하는 스니치의 종류는 매우 다양하다. 스니치는 단순히 1개의 데이터 센터(Data Center)를 가정한 것도 있고, 다수의 Data Center에 다양한 랙(Rack) 배치까지 고려한 것도 있으며, 심지어 클라우드 스택(Cloud Stack)이나 구글 클라우드(Google Cloud)와 같은 클라우드(Cloud) 서비스에 특화된 스니치도 존재한다. 이러한 스니치를 바탕으로 Cassandra는 사용자가 정의한 스키마에 따라서 어느 Data Center의 어느 Rack에다가 각각 몇 개의 복제 데이터(Replication Data)를 나누어 저장 할 것인지 등을 결정하는 것이다. 그리고 이렇게 구성된 Cassandra에 사용자가 데이터를 CRUD(Create Read Update Delete)하고자 한다면, 사용자가 해당 쿼리와 함께 지정한 일관성 수준을 통하여 데이터를 처리하게 된다.[4]
각주
- ↑ 1.0 1.1 아파치 카산드라 위키백과 - https://ko.wikipedia.org/wiki/%EC%95%84%ED%8C%8C%EC%B9%98_%EC%B9%B4%EC%82%B0%EB%93%9C%EB%9D%BC
- ↑ 2.0 2.1 2.2 2.3 2.4 2.5 엄세진, 〈Apache Cassandra 톺아보기 1편〉, 《토스트 밋업》, 2016-01-18
- ↑ Log logblsl, 〈(Cassandra)Table, Column, Partition Key Clustering Key〉, 《티스토리》, 2020-05-21
- ↑ 4.0 4.1 4.2 4.3 4.4 4.5 4.6 4.7 〈(Cassandra)카산드라 데이터 모델〉, 《블로거》, 2016-06-19
- ↑ 5.0 5.1 5.2 martin.k, 〈카산드라데이터모델〉, 《다음 블로그》, 2013-07-25
참고자료
- 아파치 카산드라 위키백과 - https://ko.wikipedia.org/wiki/%EC%95%84%ED%8C%8C%EC%B9%98_%EC%B9%B4%EC%82%B0%EB%93%9C%EB%9D%BC
- 엄세진, 〈Apache Cassandra 톺아보기 1편〉, 《토스트 밋업》, 2016-01-18
- Log logblsl, 〈(Cassandra)Table, Column, Partition Key Clustering Key〉, 《티스토리》, 2020-05-21
- 〈(Cassandra)카산드라 데이터 모델〉, 《블로거》, 2016-06-19
- martin.k, 〈카산드라데이터모델〉, 《다음 블로그》, 2013-07-25
같이 보기
- 데이터베이스 관리 시스템(DBMS)
- 노에스큐엘(NoSQL)
- 페이스북
- 아파치재단