반응형
블로그 이미지
개발자로서 현장에서 일하면서 새로 접하는 기술들이나 알게된 정보 등을 정리하기 위한 블로그입니다. 운 좋게 미국에서 큰 회사들의 프로젝트에서 컬설턴트로 일하고 있어서 새로운 기술들을 접할 기회가 많이 있습니다. 미국의 IT 프로젝트에서 사용되는 툴들에 대해 많은 분들과 정보를 공유하고 싶습니다.
솔웅

최근에 받은 트랙백

글 보관함


Composite Partition Key



composite partition key를 가지고 있는 테이블에 대해 카산드라는 partition key로 여러개의 컬럼을 사용한다. 이 컬럼들은 retrieval (검색)을 용이하게 하기 위해 partition 안에 logical set들을 구성한다. simple partition key와는 다르게 composite partition key는 어디에 데이터가 자리잡을지를 정하기 위해 두개 이상의 컬럼들을 사용한다. Composite partition key들은 single partition에 들어가기엔 데이터가 너무 클 경우 사용된다. partition key에 대해 한개 이상의 컬럼을 사용하는 것은 데이터를 chunks 나 bucket들로 파편화하게 된다. 이 데이터는 group 화 되어 있지만 더 작은 chunk들로 구성되게 된다. 이 방법은 Cassandra cluster 가 hotspotting 이거나 하나의 node에 집중적이고 반복적으로 데이터를 write 하는 경우에 유용하게 사용할 수 있다. 왜냐하면 이 경우 하나의 partition에 아주 집중적으로 writing이 이뤄질 것이기 때문이다. Cassandra는 종종 time series data를 사용하는 경우가 있고 hotspotting 이 문제화 될 수 있다. 입력되는 데이터를 bucket들에 파편화 할 때 년:월:일:시 이 4개의 컬럼을 기반으로 나뉘어지게 되는데 이럼으로서 partition에 hotspot을 줄일 수 있게 됩니다.

데이터는 partition key를 사용해서 검색 (retrieve) 하게 됩니다. 테이블에서 데이터를 검색하기 위해서는 모든 컬럼들의 값들이 partition key안에 정의 되어 있어야 합니다. (만약 secondary index 가 사용되지 않을 경우). 아래 race_year, race_name 테이블은 composition partition key로 된 primary key 안에 있습니다. 데이터를 검색하기 위해서는 두 파라미터 모두가 identify 되어야 합니다.




Composit partition key를 가지고 있는 테이블을 생성하려면 아래와 같이 하면 된다.

    CREATE TABLE 구문의 마지막에 쉼표를 넣고 PRIMARY KEY 라는 키워드를 입력한다. 그리고 그 다음에 partition key 의 컬럼 이름을 넣는다. Composite partition key인 컬럼 이름은 double parentheses(이중 괄호) 안에 넣습니다.

카산드라는 데이터의 전체 row를 partition key 에 따라서 node 에 저장합니다. 만약 하나의 partition에 너무 많은 데이터가 있다면 그리고 그 데이터들을 여러개의 노드들에 분산시키기를 원한다면 composite partition key를 사용합니다.



Using a composite partition key

쿼리를 실행시킬 때 결과를 정렬 시켜야 되는 컬럼이 있다면 이 컬럼을 만들 때 primary key 안에 이 composite partition key를 사용하세요. 아래 예제는  rank_by_year_and_name 이라는 테이블을 만듭니다. 여기에는 레이스에서 이긴 사이클 선수의 순위와 이름이 저장될 겁니다. 이 테이블은 primary key의 composition partition key 로 정의된 컬럼으로 race_year 와 race_name을 사용하고 있습니다. 쿼리는 year와 race name 값을 사용해서 레이스에서 우승한 사이클 선수의 순위를 알아내는데 사용됩니다.

composite partition key 테이블은 아래와 같이 두가지 방법으로 생성할 수 있습니다.



Procedure

    * cycling 키스페이스에 rank_by_year_and_name 이라는 테이블을 생성합니다. composite partition key 로 race_year 와 race_name을 사용합니다. 이 테이블에는 primary key로 사용하는 컬럼인 rank 가 있습니다. 테이블을 만들기 전에, USE 구문을 사용해서 해당 키스페이스로 들어가세요. 이 예제에는 맨 마지막에 primary key 를 정의 합니다. PRIMARY KEY 에서 이중괄호를 사용한 것을 기억하시기 바랍니다.

    cqlsh> USE cycling;
    CREATE TABLE rank_by_year_and_name (
    race_year int,
    race_name text,
    cyclist_name text,
    rank int,
    PRIMARY KEY ((race_year, race_name), rank)
    );




  

USE 구문을 생성해서 해당 키스페이스 안에 들어가는것 대신에 CREATE TABLE 구문에서 키스페이스 이름을 사용함으로서 해당 키스페이스에 테이블을 만들 수 있습니다.

    cqlsh> CREATE TABLE cycling.rank_by_year_and_name (
    race_year int,
    race_name text,
    cyclist_name text,
    rank int,
    PRIMARY KEY ((race_year, race_name), rank)
    );



Compound Primary Key



compound primary key를 가지고 있는 테이블을 만들려면 simple 이나 composite partition key를 사용합니다. 거기에 추가로 clustering 컬럼들을 정의하게 됩니다. Clustering 은 clustering 컬럼들을 정의한 것에 근거해 각 partition 안에 데이터들을 정렬하는 storage engine process 입니다. 대개 컬럼들은 알파벳 오름차순으로 정렬 됩니다. 일반적으로 데이터들의 different grouping은 simplistic choice 보다 더 좋은 read 와 write 결과를 가져오게 됩니다.

데이터는 Cassandra cluster 전체에 걸쳐 distributed 된다는 것을 기억해 두세요. 만약 적은 데이터를 얻기 위해 전체 partition을 read 해야만 한다면 어플리케이션은 large partition으로부터 데이터를 검색해야 하는 부담을 갖게 될 것입니다. physical node 에서 partition key 에 대한 row들이 clustering column들에 근거해 정렬돼 저장됐다면 row들을 검색하는게 아주 효율적일 겁니다. clustering column을 사용하는 테이블내에서 데이터를 그룹핑하는 것은 관계형 데이터베이스의 JOIN 과 비슷하면서 오직 한개의 테이블만 사용하게 됨으로서 훨씬 좋은 performance를 보여줍니다. 이 테이블은 partition key로서 category를 사용하고 clustering column으로서 points를 사용합니다. 각각의 category에 대해 point들은 내림차순으로 정렬된다는 것을 기억해 두세요.



partition으로부터 데이터를 검색하는 것은 clustering column들과 함께 다용도로 사용됩니다. 위 예제를 예로 들면 one-day-races 에 대해 200보다 큰 point 값들을 검색할수 있습니다.

compound primary key를 가지고 있는 테이블을 생성하려면 아래와 같이 합니다.

    * CREATE TABLE 구문의 마지막 컬럼 다음에 PRIMARY KEY 키워드를 넣고 그 키의 column 이름을 넣습니다. 그 컬럼 이름은 괄호로 감싸게 됩니다.



Using a compound primary key

정렬된 결과를 받는 쿼리를 사용할 컬럼을 생성할 때 compound primary key를 사용합니다. 만약 pro cycling 예제가 관계형 데이터베이스용으로 디자인 된다면 races 테이블에 대해 cyclists 테이블에 foreign key를 만들에 서로 관계를 맺게 할 겁니다. 카산드라에서는 이 데이터를 denormalize 하게 됩니다. 왜냐하면 distributed system 에서는 JOIN 은 그렇게 좋은 퍼포먼스를 보여주지 못하기 때문입니다. 이후에 카산드라에서 퍼포먼스를 향상시키기 위해 사용하는 다른 schema를 보게 될 겁니다. Collections와 indexes 는 두개의 데이터 모델링 방법입니다. 아래 예제는 각 race category 에 대해 사이클 선수의 성과 ID, points 가 저장되는 cyclist_category 테이블을 생성합니다. 이 테이블은 partition key로 category를 사용하고 single clustering column으로는 points를 사용합니다. 이 테이블은 points로 정렬된 결과를 검색할 수 있습니다. 검색 내용은 하나의 카테고리 안에 사이클 선수와 그들의 points 리스트가 될 겁니다.

compound primary key 테이블은 아래와 같이 두가지 방법으로 생성할 수 있습니다.



Procedure

    * Compound primary key 를 가지고 있는 table을 만들려면 두개 이상의 primary key를 사용합니다. 이 예제에서는 points에 대해 내림차순으로 보기 위해 WITH CLUSTERING ORDER BY라는 추가적인 절을 사용하고 있습니다. 오름차순이 저장하기에는 좀 더 효율적입니다. 하지만 storage engine의 특성상 내림차순이 더 빠릅니다.

    cqlsh> USE cycling;
    CREATE TABLE cyclist_category (
    category text,
    points int,
    id UUID,
    lastname text,    
    PRIMARY KEY (category, points)
    ) WITH CLUSTERING ORDER BY (points DESC);





    USE 구문을 사용해서 해당 키스페이스 안에 들어가지 않고 테이블을 만들려면 keyspace 이름을 아래와 같이 명시해 주면 됩니다.

    cqlsh> CREATE TABLE cycling.cyclist_category (
    category text,
    points int,
    id UUID,
    lastname text,    
    PRIMARY KEY (category, points)
    ) WITH CLUSTERING ORDER BY (points DESC);

    Note: category와 points의 조합은 cyclist_category 테이블내에서 row를 구별해 주는 unique 한 구분 방법입니다. 같은 category에 대해 한개 이상의 row들이 존재할 수 있는데 이 경우 row들은 다른 points 값을 가지고 있는 경우 일 겁니다. 위 예제를 다시 보세요. 이 데이터를 저장하기 위한 최적화 된 데이터 모델링 인가요? 어떤 condition들이 에러를 유발할 수 있을 까요?



Creating a counter table



Counter는 여러 데이터 모델들에 유용합니다. 예를 들어, 어느 회사에서 회사 홈페이지의 웹페이지 뷰 숫자를 하는 경우 혹은 Scorekeeping 어플리케이션은 온라인으로 현재 게임하는 유저의 숫자를 저장하거나 게임에 접속한 유저의 숫자를 저장하기 위해 counter를 사용할 수 있을 겁니다. 아래 테이블은 id를 primary key로 하고 counter 테이블의 popularity필드에 thumbs up/down 에 근거한 cyclist의 popularity 를 관리하는데 counter 가 사용됨을 보여 줍니다.



distributed database의 Tracking count를 살펴보는 건 흥미로운 일입니다. 카산드라에서는 counter value는 Memtable 안에 위치할 것입니다. 그리고 특정 시기에 한개 이상의 SSTable에 commit 하게 됩니다. 노드들 사이에서 replication (응답) 하는 것은 특정 상황에서 consistency 문제를 야기시킬 수 있습니다. 카산드라의 counter는 counter의 기능을 개선하기 위해 Cassandra 2.1 에서 redesign 됐습니다.다. 그 내용을 자세히 보려면 "What’s New in Cassandra 2.1: Better Implementation of Counters" 를 보세요.

counter는 증가되는 숫자를 저장하기 위한 특별한 column 입니다. counter는 integer 값을 가지면 오름차순/내림차순으로 사용할 수 있습니다. counter는 다른 컬럼들에서 서로 다르게 구현될 수 있기 때문에 counter 컬럼들은 dedicated table에서만 정의 될 수 있습니다.

카산드라 2.1 이상에서는 cassandra.yaml 파일에 여러 counter-related setting을 configure 할 수 있습니다.


Note : 카산드라 2.0.x 에서 사용한 replicate_on_write 테이블 프로퍼티는 카산드라 2.1 에서는 사용하지 않습니다.

counter 컬럼은 index 되거나 delete 될 수 없습니다. counter 컬럼에 데이터를 로드하기 위해서는 혹은 counter의 값을 증가/감소 시키기 위해서는 UPDATE command를 사용합니다. Cassandra는 counter 컬럼을 업데이트 할 때 USING TIMESTAMP 나 USING TTL 은 reject 합니다.

counter 컬럼을 가지고 있는 테이블을 생성하기 위해서는 이렇게 하면 됩니다.

    * counter 와 non-counter 컬럼들을 정의하기 위한 CREATE TABLE을 사용하는데 PRIMARY KEY 정의 하는 일 부분으로 non-counter column들을 사용합니다.




Using a counter

counter 컬럼에 데이터를 로드하기 위해서 혹은 counter 값을 증가/감소시키기 위해서는 UPDATE 명령어를 사용합니다. Cassandra는 counter 컬럼을 업데이트 할 때 USING TIMESTAMP 나 USING TTL 은 reject 합니다.



Procedure

    * counter column을 갖는 테이블 생성.

    cqlsh> USE cycling;
    CREATE TABLE popular_count (
      id UUID PRIMARY KEY,
      popularity counter
      );





    * counter column에 데이터를 로딩하는 방법은 다른 테이블과 좀 다릅니다. 해당 데이터는 insert 되는 것이 아니라 update 된다.

    UPDATE cycling.popular_count
     SET popularity = popularity + 1
     WHERE id = 6ab09bec-e68e-48d9-a5f8-97e6fb4c9b47;

    * counter value를 잘 살펴보세요. popularity 가 1의 값을 가지고 있는 부분을 잘 보세요.


    SELECT * FROM cycling.popular_count;





    * 추가적인 증가/감소는 counter column의 value를 바꿀 겁니다.




Create table with COMPACT STORAGE

테이블을 생성하면서 compound primary key를 할 때 저장된 모든 데이터에 대해 그것과 함께 column 이름이 저장될 필요가 있습니다. 디스크에 하나의 컬럼으로서 각 컬럼에 대응하는 저장된 각각의 non-primary key 컬럼 대신에 디스크에 전체 row를 한개의 컬럼에 저장합니다. 디스크 공간을 절약해야 할 경우 WITH COMPACT STORAGE를 사용합니다. 그러면 legacy (Thrift) storage engine format 으로 데이터를 저장합니다.

CREATE TABLE sblocks (
  block_id uuid,
  subblock_id uuid,
  data blob,
  PRIMARY KEY (block_id, subblock_id)
)
WITH COMPACT STORAGE;





Compact storage 를 사용하면 compount primary key가 아닌 한개 이상의 컬럼을 정의해야 되는것을 막아 줍니다. compact 되지 않은 primary key를 사용하는 compact table은 primary key 가 아닌 여러개의 컬럼들을 가질 수 있습니다.

compound primary key를 사용하는 compact table은 적어도 한개 이상의 clustering 컬럼을 정의 해야만 합니다. compact table이 생성되고 나면 컬럼들은 추가되거나 제거될 수 없습니다. WITH COMPACT STORAGE 를 사용하지 않으면 CQL은 non-compact storage로 테이블을 생성합니다.

Collection과 static 컬럼들은 COMPACT STORAGE tables 에 사용될 수 없습니다.



Table schema collision fix

Dynamic schema 생성이나 update는 에러를 유발하는 schema collision 을 발생 시킬 수 있습니다.

Procedure

    1. schema match를 위해 모든 노드에서 전체적으로 재 시작을 합니다. 모든 노드가 있는 클러스터에서 nodetool을 실행합니다. 그리고 한개의 schema 버전이 있는지 확인합니다.

    2.각 노드에서 - data directory 를 체크 한다. - 질문에 해당하는 테이블에 대한 두개의 디렉토리를 찾는다. 만약 한개의 디렉토리만 있다면 다음 노드로 이동한다. 만약에 그 노드에 두개 이상의 디렉토리가 있다면 오래된 테이블 디렉토리는 업데이트 이전것이고 새것은 update 이후를 위한 것이다. 다음으로 간다.

    $ cqlsh -e "SELECT * FROM system.schema_column_families" |grep <tablename>

    3. 어떤 cf_id (column family ID) 가 system.schema_columnfamilies 의 최신 테이블 ID 인지 찾는다. 이 column family ID는 결과의 5번째 컬럼이다.
   
    Move the data from the older table to the newer table's directory and remove the old directory. Repeat this step as necessary.
    4. 이전 테이블의 데이터를 최근 테이블의 디렉토리로 옮기고 이전 디렉토리는 지운다. 이 작업을 다른 노드들에서도 계속 반복한다.

    5. nodetool refresh 를 실행한다.

반응형

Comment