본문 바로가기
DataBase/NoSQL

오 아름답도다. Cassandra

by Dev. Jkun 2011. 2. 11.
반응형

출처 : http://blog.naver.com/naverdev/120116325495

 

마법과도 같이이 글을 읽고 난 여러분들은 다음과 같은 것들을 할 수 있게 됩니다.

 NoSQL 세계의 선봉장인 Cassandra 데이터베이스의 존재를 인지한다.

 Cassandra의 특징을 간략하게 이해한다.

 Cassandra를 간단하게 설치하고 기본 기능을 이용하는 방법을 안다.

 


 
 


Cassandra

NoSQL 세계에서 Cassandra의 위치는 절대적입니다. Voldemort, MongoDB, TokyoCabinet/Tyrant 등 쟁쟁한 NoSQL 제품군 중에서 왜Cassandra가 독보적인 위치를 점유하고 있는지 말씀드리고 싶습니다.

Cassandra는 원래 Facebook에서 만들었던 오픈소스 Distributed Database였습니다현재는 Apache 프로젝트로 자리를 옮겼습니다.

Cassandra의 가장 큰 장점은 ‘상대적으로 다른 NoSQL DB에 비하여 레퍼런스가 많고활발하게 개발되고 있다.’는 점입니다상당히 다양한 분야와 많은 기업에서 Cassandra를 사용하고 있으며, 2010년 아파치 Top-level project로 승격되었기 때문에 개발 과정이 매우 잘 관리되고 있다고 할 수있습니다.

Cassandra의 대표적인 활용 사례는 Facebook inbox search(페이스북 사용자 프로필 내부 검색)입니다여기서 사용하는  Cassandra클러스터 규모는 600개 이상의 CPU 코어와 120 테라바이트 이상의 용량을 포함하고 있습니다.[1] Twitter에서도 일부 서비스의 스토리지를 Cassandra로 전환하려는 움직임이 있으며[2]소셜 뉴스 사이트인 Digg.com(Alexa.com 순위 98)에서도 주요 서비스에 사용되고 있습니다.[3] 이 외에 텍스트 검색 오픈소스인 Lucene에 스토리지 부분을 Cassandra로 변환한 Lucandra도 만들어 지는 등[4] 여러 방면에서 사용된 경험이 많이 축적되어 있습니다.

 

특징

Cassandra는 구글에서 만든 BigTable[5]과 아마존에서 만든 Dynamo[6]의 특징을 합쳐서 생겨난 데이터베이스입니다구체적으로는BigTable의 특징인 Column-oriented model, memTable 그리고 SSTable 방식의 쓰기 방식을 채택하였고, Dynamo의 특징인 높은 가용성속도와 일관성 사이의 trade off 조절 기능을 도입하였습니다.[7]

 

Scalability

Scalability Cassandra의 가장 큰 특징입니다장비를 추가하고 제거하는 과정은 매우 단순합니다단지 새로운 장비를 추가하고 설정을 바꾼 후 Cassandra를 재시작하면 됩니다. Cassandra는 노드가 추가되면 자동적으로 Consistent hashing을 통해 각 노드가 가진 키의 개수를 맞춥니다.

 

Fault-Tolerant

Cassandra의 또 다른 특징은 single point of failure가 없다는 것입니다이 때문에 어떤 노드에 장애가 발생해도 전체 시스템은 멈추지 않습니다.

여러 노드가 한꺼번에 멈춰도 운 나쁘게 같은 데이터의 Replica가 모두 멈추는 것이 아니라면 read에는 문제가 없고, write replica에 관계없이 항상 가능합니다.

 

Consistency-Partition tolerance 사이의 균형

대부분의 NoSQL 데이터베이스들은 Availability Partition Tolerance를 달성하고, Consistency를 약하게 지원하는 방식을 채택했으나Cassandra Read replica count, write replica count를 설정하는 방식으로 Consistency Availability 사이의 균형을 사용자가 선택할 수 있도록 하였습니다설정하기에 따라 Consistency를 강화시켜 사용할 수도 있고, Availability를 강화시켜 사용할 수도 있습니다물론 두 가지 모두를 강화시키는 것은 불가능합니다.(CAP Theorem)

 

 

Column-oriented Data Model

Cassandra Column-oriented Data Model을 사용합니다.(Column-Oriented에 대한 설명은 'Database Technology for Large Scale Data편에 자세히 나와 있습니다.)


간단히 말해 RDBMS row를 레코드 형태로 저장하지만, Column-oriented Database에서는 Column을 모아 저장합니다당연히 연관된 데이터를 읽기에 적합합니다

각 컬럼은 name, value, timestamp를 가지고 있습니다. Timestamp를 제외하면 각 Key Java HashMap, 또는 HashMapHashMap 구조가 붙어있는 형태입니다.

 

아래 그림을 보면 이해가 빠를 것입니다.


 

데이터의 key ‘안일운’, ‘김정일이며, ‘안일운’ 키가 가진 컬럼들은 ‘’, ‘용돈이며 ‘김정일’ 키가 가진 컬럼들은 ‘’, ‘용돈’, ‘부인’, ‘차고입니다각 컬럼은 컬럼 제목(용돈)과 컬럼 값(178, 50만원)을 가지고 있습니다.‘김정일키에는 ‘안일운키에는 없는 ‘부인 ‘차고’ 컬럼이 있습니다. ‘차고’ 컬럼은 여러 개의 하위 컬럼을 또 가질 수 있습니다컬럼을 여러 개 포함하는 컬럼은 SuperColumn이라고 합니다.

            

178

용돈

50


15

용돈

30

부인

4

차고

2

람보르기니

4

벤츠

2

탱크

실제로 Cassandra에서 Column-oriented Data Model을 다루다 보면 Keyspace, Column Family과 같은 용어부터 Cassandra 고유의 자료형(Type) 등 관계형 데이터베이스 세계에서 경험이 많았던 분들에게는 생소한 것들이 많이 등장합니다더 자세한 설명과 용어 해설은Cassandra 위키 페이지[8] 및 기타 웹사이트들[9,10,11]를 통해 읽으실 수 있습니다.

 

High Performance

Write

Cassandra Read 연산보다 Write연산이 훨씬 더 빠릅니다키의 위치에 따라 데이터 파일 중간 중간에 row를 끼워 넣는 방식이 아니라, SSTable(Sorted String Table)에 데이터를 Append한 후 SSTable을 통째로 저장하기 때문입니다데이터를 넣을 위치를 찾을 필요가 없어 일반적으로 관계형 데이터베이스보다 더 빠른 쓰기 속도를 보여줍니다.

 

Read

Write 연산으로 만들어진 SSTable은 일정 크기가 되면 key에 따라 정렬된 후 디스크에 기록됩니다. Read 연산 역시 기존 관계형 데이터베이스와는 달리 SSTable bloom filter(어떤 데이터가 어떤 집합에 속해있는지 파악하기 위한 방법)를 통해 읽어올 데이터를 가지고 있는지 알아 낸 후 인덱스를 통해 정보를 읽어냅니다.

 

아직까지 공식적인 Read/Write 속도 테스트 결과는 없는 것으로 알고 있습니다다만 기존 관계형 데이터베이스(CUBRID, MySQL, Oracle)와의 속도 직접 비교는 무의미할 수 있습니다데이터를 다루는 용도 및 목적 자체가 관계형 데이터베이스와 크게 다를 수 있기 때문입니다.

 

Lockless

Read/Write 연산 모두 lockless, 즉 한 스레드가 연산을 수행하는 동안 다른 스레드가 해당 데이터에 접근하지 못하는 경우가 없습니다동시성 문제(concurrency issue)로 인한 성능저하는 발생하지 않습니다.

 

제약사항

특징들을 나열해 놓고 보면 마치 개발자들이 원하고 원하던 꿈의 스토리지처럼 들리지만제약사항도 있습니다.

무엇보다 Cassandra 1.0 버전이 아직 나오지 않은개발 중인 오픈 소스입니다상용화된 관계형 데이터베이스또는 MySQL같은 유구한 역사를 자랑하는 제품에 비해 기능 자체는 많이 부족한 상황입니다데이터를 Cassandra에 보관시켜놓고 운영 및 관리를 하다 보면MySQL의 편리한 도구들이 금방 그리워집니다버전 업 속도로 빠르고 기능추가기존 버전의 문제점 해결이 속속 이루어지고는 있으나 실제 서비스에 사용되는 코드의 일부가 되기에는 부족한 점이 많습니다. Cassandra를 활용하는 기업들도 주력이 아닌 2선급 서비스에 적용하거나백엔드 인프라로 활용하는 경우가 대부분입니다.

하지만 이런 단점에도 불구하고, Cassandra NoSQL 데이터베이스 중 가장 많은 기능을 가지고 있고 유망한 오픈 소스임에는 이의를 제기할 수 없다고 생각합니다.

 

Cassandra 설치와  동작

Cassandra의 설치는 비교적 간단한 편입니다.

Cassandra 홈페이지(http://cassandra.apache.org)에서 바이너리 파일을 다운로드 받은 후 압축을 풀기만 하면 됩니다.

Cassandra를 구동시키기 전 설정파일을 고쳐야합니다.

시험적으로 서버 1대만을 사용할 경우기본 설정 파일을 그대로 써도 문제가 없습니다다만 Cassandra 데이터 파일과 로그파일이 들어갈 디렉토리가 존재하는지 확인하고없다면 만들어 줄 필요는 있습니다.설정파일인 conf/storage-conf.xml을 보면CommitLogDirectory(로그 파일이 들어갈 디렉토리를 지정항목과 DataFileDirectories(실제 데이터 파일이 들어갈 디렉토리를 지정항목을 적절히 수정해 주면 됩니다.

<CommitLogDirectory>

     /home1/data/cassandra/commitLog

</CommitLogDirectory>

<DataFileDirectories>

  <DataFileDirectory>

   /home1/data/cassandra/dataFile

  </DataFileDirectory> 

</DataFileDirectories>

만약 서버 2대 이상을 사용하는  클러스터를 구성하고 싶다면각 서버에 Cassandra를 설치한 후 설정파일을 각각 고쳐주어야 할 필요가 있습니다클러스터 구성 시 추가적으로 고쳐주어야 할 설정은 시드 설정과 통신 설정입니다.

 

시드(Seed), Cassandra 클러스터에 새로 추가되는 노드가 들어올 때이 신입 노드에게 노드들이 연결된 링 구조를 가르쳐 주는 역할을 수행합니다설정파일의 Seeds 항목을 보면 이를 설정할 수 있습니다아래 예시에서는 krs1164.nhncorp.com 호스트를 가진 장비를 시드로 지정하였습니다여러 개를 지정할 수도 있습니다.

<Seeds>

  <Seed>krs1164.nhncorp.com</Seed> 

</Seeds>

또한 클러스터의 노드들이 서로 통신할 때 사용하는 Gossip(클러스터의 링 구조에 관한 정보를 주고 받는데 사용) Thrift(클러스터가 실제로 저장하는 데이터를 교환하는데 사용) IP 인터페이스를 설정해야 합니다이들은 각각 ListenAddress ThriftAddress 항목에서 지정할 수 있습니다.

아래 예시에서는 krs1164.nhncorp.com에서 다른 Cassandra 클러스터의 노드와 통신하기 위해 20007 20008 포트를 사용하도록 해 놓았습니다.

<ListenAddress>

  krs1164.nhncorp.com

</ListenAddress>

 

<StoragePort>20007</StoragePort>

<ThriftAddress>

  krs1164.nhncorp.com

</ThriftAddress>

<ThriftPort>20008</ThriftPort>

 

 

설정이 끝났으면 클러스터 내 모든 노드에서 각각 /bin/cassandra를 실행하면 됩니다기동되는 시간은 그리 길지 않습니다.

모든 노드가 정상적으로 가동되고 나면 Cassandra 클러스터가 제대로 동작하고 있는지 확인해 봅니다. /bin/nodetool을 사용하면 운영에 필요한 정보들을 얻을 수 있습니다아래 예시에서는 이미 어느 정도 사용한 Cassandra 클러스터의 링 구조를 보여주고 있습니다.

  


여기서 설명한 것 외에도설정파일에서는 많은 것들을 변경 할 수 있습니다. Memtable(Cassandra에서 key에 해당하는 value(Row)에 대한 cache) 크기나 응답대기 시간 같은 내부 동작 설정부터 데이터 저장 구조까지 모두 설정하도록 되어 있으므로 필요에 따라 많은 수정을 거쳐야 합니다.

 

설치와 설정파일에 관련된 정보가 완벽하게 정리된 가이드 같은 것은 아직 없습니다최신 버전이 0.7.0 beta인 오픈 소스라 어쩔 수 없는 상황이라고도 보입니다만 체계적인 문서화가 미흡한 점은 분명 아쉬운 부분입니다. Cassandra 개발 웹페이지(http://cassandra.apache.org)의 위키메일링 리스트 등을 열심히 탐독하다 보면 다양한 설정과 운영 방법에 대한 정보를 더 얻을 수 있습니다.

 

API 사용

Cassandra는 클라이언트와의 통신을 위해 Thrift API를 제공합니다. Thrift Facebook에서 개발한 RPC이며 다양한 언어를 지원합니다. Thrift도 현재 아파치 인큐베이터를 통해 개발되고 있습니다.

Cassandra 소스코드를 다운로드 받아놓았다면 이 소스코드에 포함된 Cassandra 클라이언트 패키지와 Thrift를 이용하여 클라이언트 프로그램을 작성할 수 있습니다.

 

Cassandra의 기본 설정파일인 conf/storage-conf.xml을 보면 Keyspace1이라는 키스페이스(MySQL Database에 해당하위에Standard1이라는 Column Family(MySQL Table에 해당)가 이미 정의되어 있습니다.

이 키스페이스에 값을 쓰고읽어오는 자바 소스코드는 다음과 같습니다[14]에서 발췌한 내용을 일부 수정하였습니다소스코드는 찬찬히 들여다보시면 어렵지않게 이해하실 수 있으실 것입니다.

 

 

자바 뿐 아니라 PHP, C++, Python 등에서도 Cassandra의 모든 기능을 사용할 수 있습니다다른 언어로 된 소스코드도 샘플코드 페이지(http://wiki.apache.org/cassandra/ThriftExamples )에서 찾아보실 수 있습니다.


 

package cassandra.clientest;

 

import java.io.UnsupportedEncodingException;

import java.util.Date;

import java.util.List;

 

import org.apache.cassandra.thrift.Cassandra;

import org.apache.cassandra.thrift.Column;

import org.apache.cassandra.thrift.ColumnOrSuperColumn;

import org.apache.cassandra.thrift.ColumnParent;

import org.apache.cassandra.thrift.ColumnPath;

import org.apache.cassandra.thrift.ConsistencyLevel;

import org.apache.cassandra.thrift.InvalidRequestException;

import org.apache.cassandra.thrift.NotFoundException;

import org.apache.cassandra.thrift.SlicePredicate;

import org.apache.cassandra.thrift.SliceRange;

import org.apache.cassandra.thrift.TimedOutException;

import org.apache.cassandra.thrift.UnavailableException;

import org.apache.thrift.TException;

import org.apache.thrift.protocol.TBinaryProtocol;

import org.apache.thrift.protocol.TProtocol;

import org.apache.thrift.transport.TSocket;

import org.apache.thrift.transport.TTransport;

 

public class Main {

 

  public static final String UTF8 = "UTF8";

 

  public static void main(String[] args) throws UnsupportedEncodingException,

                InvalidRequestException, UnavailableException, TimedOutException,

                TException, NotFoundException {

    // 접속할 노드를 지정하여 소켓을 정의합니다.

    // 접속할 호스트는 Cassandra 클러스터 중 어떤 것이 되어도 상관없습니다.

    TTransport tr = new TSocket("krs1164.nhncorp.com"20008);

    TProtocol proto = new TBinaryProtocol(tr);

    Cassandra.Client client = new Cassandra.Client(proto);

    tr.open();

 

    String keyspace = "Keyspace1";

    String columnFamily = "Standard1";

    String keyUserID = "1";

 

    // insert data

    long timestamp = System.currentTimeMillis();

 

    ColumnPath colPathName = new ColumnPath(columnFamily);

    colPathName.setColumn("fullName".getBytes(UTF8));

 

    client.insert(keyspacekeyUserIDcolPathName,

        "Chris Goffinet".getBytes(UTF8), timestamp, ConsistencyLevel.ONE);

 

    ColumnPath colPathAge = new ColumnPath(columnFamily);

    colPathAge.setColumn("age".getBytes(UTF8));

 

    client.insert(keyspacekeyUserIDcolPathAge"24".getBytes(UTF8),

                                timestamp, ConsistencyLevel.ONE);

 

    // read single column

    System.out.println("single column:");

    Column col = client.get(keyspacekeyUserIDcolPathName,

                                ConsistencyLevel.ONE).getColumn();

 

    System.out.println("column name: " new String(col.nameUTF8));

    System.out.println("column value: " + new String(col.valueUTF8));

    System.out.println("column timestamp: " + new Date(col.timestamp));

 

    // read entire row

    SlicePredicate predicate = new SlicePredicate();

    SliceRange sliceRange = new SliceRange();

    sliceRange.setStart(new byte[0]);

    sliceRange.setFinish(new byte[0]);

    predicate.setSlice_range(sliceRange);

 

    System.out.println(" row:");

    ColumnParent parent = new ColumnParent(columnFamily);

    List<ColumnOrSuperColumn> results = client.get_slice(keyspace,

                                keyUserIDparentpredicate, ConsistencyLevel.ONE);

    for (ColumnOrSuperColumn result : results) {

      Column column = result.column;

          System.out.println(new String(column.name, UTF8) + " -> "

                    new String(column.value, UTF8));

    }

    tr.close();

  }

}


가만히 살펴보면 범용 프레임워크인 Thrift를 이용해 Cassandra 노드와 통신하는 것은 그리 깔끔하지 못하다는 느낌을 받을 수 있습니다.

뿐만 아니라 한 대의 노드가 아닌 클러스터를 상대로 데이터 입출력 작업을 하다 보면 어떤 노드가 사용불능일 때의 처리, Connection Pooling 등의 기능이 필요해 질 때가 많습니다.

이러한 필요에 의해 Cassandra 클러스터에 접근하는 Thrift API를 한번 더 포장해서 만든 Cassandra 클라이언트 API도 공개되어 있습니다.  Cassandra 고수준 클라이언트 소개 페이지(http://wiki.apache.org/cassandra/ClientOptions)에서 찾아 볼 수 있으며자바용 클라이언트로는 Hector[15] Pelops[16]가 유명합니다개인적인 경험으로는 Pelops 쪽이 좀 더 깔끔한 클라이언트 코드를 만드는 데 도움이 되었으나, Cassandra 버전업에 의해 기능이 변경되거나 추가되는 것을 반영하는 속도는 Hector에 비해서 좀 느린 감도 있었습니다.

Reference

[1] http://www.facebook.com/note.php?note_id=24413138919

[2] http://nosql.mypopescu.com/post/407159447/cassandra-twitter-an-interview-with-ryan-king

[3] http://about.digg.com/node/564

[4] http://blog.sematext.com/2010/02/09/lucandra-a-cassandra-based-lucene-backend/

[5] Fay Chang, Jeffrey Dean, Sanjay Ghemawat, Wilson C. Hsieh, Deborah A. Wallach, Mike Burrows, Tushar Chandra, Andrew Fikes, and Robert E. Gruber, “Bigtable : A Distributed Storage System for Structured Data”, OSDI'06: Seventh Symposium on Operating System Design and Implementation, Seattle, WA, November, 2006.

[6] Giuseppe DeCandia, Deniz Hastorun, Madan Jampani, Gunavardhan Kakulapati, Avinash Lakshman, Alex Pilchin, Swami Sivasubramanian, Peter Vosshall and Werner Vogels, “Dynamo: Amazon's Highly Available Key-Value Store”, in the Proceedings of the 21st ACM Symposium on Operating Systems Principles, Stevenson, WA, October 2007.

[7] Avinash Lakshman, Prashant Malik, “Cassandra – Adecentralized Structured Storage System”,http://www.cs.cornell.edu/projects/ladis2009/papers/lakshman-ladis2009.pdf

[8] http://wiki.apache.org/cassandra/DataModel

[9] http://arin.me/blog/wtf-is-a-supercolumn-cassandra-data-model

[10] http://www.sodeso.nl/?p=108

[11] http://www.sodeso.nl/?p=207

[12] http://github.com/rantav/hector

[13] http://github.com/s7/scale7-pelops

[14] http://wiki.apache.org/cassandra/ThriftExamples

반응형

댓글