데이터 정의

데이터 정의 (Data Definition)

CQL은 데이터를 테이블에 저장하고, 테이블의 스키마가 테이블 안의 데이터 배치를 정의해요. 테이블은 키스페이스(keyspace) 안에 위치합니다. 키스페이스는 그 키스페이스의 모든 테이블에 적용되는 옵션을 정의합니다. 복제 전략(replication strategy)은 복제 계수(replication factor)와 함께 중요한 키스페이스 옵션이에요. 좋은 일반 규칙은 애플리케이션마다 키스페이스 하나를 쓰는 것입니다. 활성 애플리케이션에 대해 클러스터가 키스페이스 하나만 정의하는 것이 흔합니다.

이 섹션에서는 키스페이스와 테이블을 생성, 수정, 제거하는 데 사용되는 문을 설명할게요.

출처: 문서

본문

공통 정의

키스페이스와 테이블의 이름은 다음 문법으로 정의됩니다:

keyspace_name::= name
table_name::= [keyspace_name '.' ] name
name::= unquoted_name | quoted_name
unquoted_name::= re('[a-zA-Z_0-9]\{1, 48}')
quoted_name::= '"' unquoted_name '"'

키스페이스와 테이블 이름 모두 영숫자 또는 밑줄 문자로만 구성되어야 하고 비어 있을 수 없어요. 키스페이스 이름은 48자로 제한되고, 테이블 이름은 222자로 제한됩니다(이 한계는 주로 키스페이스·테이블 이름을 포함할 수 있는 파일 이름이 특정 파일시스템의 한계를 넘지 않도록 하기 위한 것입니다). 기본적으로 키스페이스와 테이블 이름은 대소문자를 구분하지 않지만(myTablemytable과 동일), 큰따옴표를 사용하면 대소문자 구분을 강제할 수 있어요("myTable"mytable과 다릅니다).

또한 테이블은 항상 키스페이스의 일부이며, 테이블 이름은 그것이 속한 키스페이스로 완전히 자격을 갖춘(fully-qualified) 형태로 제공될 수 있습니다. 완전히 자격을 갖추지 않으면 테이블은 현재 키스페이스에 있는 것으로 간주됩니다(USE 문 참고).

또한 컬럼의 유효한 이름은 다음과 같이 정의됩니다:

column_name::= identifier

다음 섹션에서 사용할 문 옵션(options)의 개념도 정의합니다:

options::= option ( AND option )*
option::= identifier '=' ( identifier
	| constant
	| map_literal )

CREATE KEYSPACE

키스페이스는 CREATE KEYSPACE 문으로 생성됩니다:

create_keyspace_statement::= CREATE KEYSPACE [ IF NOT EXISTS ] keyspace_name
	WITH options

예를 들어:

CREATE KEYSPACE excelsior
   WITH replication = {'class': 'SimpleStrategy', 'replication_factor' : 3};

CREATE KEYSPACE excalibur
   WITH replication = {'class': 'NetworkTopologyStrategy', 'DC1' : 1, 'DC2' : 3}
   AND durable_writes = false;

이미 존재하는 키스페이스를 만들려 하면 IF NOT EXISTS 옵션을 사용하지 않는 한 오류가 반환됩니다. 사용하면 키스페이스가 이미 존재할 때 문은 no-op이 됩니다.

지원되는 옵션:

이름 종류 필수 기본값 설명
replication map yes n/a 키스페이스에 사용할 복제 전략과 옵션(아래 자세히)
durable_writes simple no true 이 키스페이스의 업데이트에 커밋 로그를 사용할지 여부(이 옵션을 끄는 것은 사용자 책임입니다!)

replication 속성은 필수이며, 원하는 복제 전략 클래스를 정의하는 'class' 하위 옵션을 포함해야 해요. 나머지 하위 옵션은 사용되는 복제 전략에 따라 달라집니다. 기본적으로 Cassandra는 다음 'class' 값을 지원합니다:

SimpleStrategy

데이터를 클러스터 전체에 퍼뜨리기 위한 복제 계수를 정의하는 단순한 전략이에요. 데이터센터 배치를 존중하지 않고 쿼리 지연 시간이 크게 달라질 수 있어 프로덕션에는 일반적으로 현명한 선택이 아닙니다. 프로덕션에는 NetworkTopologyStrategy를 사용하세요. SimpleStrategy는 단일 필수 인자를 지원합니다:

하위 옵션 타입 설명
'replication_factor' int 범위마다 저장할 리플리카 수

NetworkTopologyStrategy

각 데이터센터에 대해 독립적으로 복제 계수를 설정하는 프로덕션 준비된 복제 전략이에요. 나머지 하위 옵션은 키(key)가 데이터센터 이름이고 값이 관련 복제 계수인 키-값 쌍입니다. 옵션:

하위 옵션 타입 설명
'' int 제공된 데이터센터의 범위마다 저장할 리플리카 수
'replication_factor' int

나중에 키스페이스를 변경하고 replication_factor를 바꾸면, 자동 확장(auto-expansion)은 안전을 위해 새 데이터센터만 추가하고 기존 데이터센터를 변경하거나 제거하지 않아요(클러스터에 더 이상 없어도). replication_factor를 설정하면서 데이터센터를 제거하려면, 리플리카를 0으로 만들고 싶은 데이터센터를 명시적으로 0으로 설정하세요.

DC1과 DC2 두 데이터센터로 자동 확장하는 예:

CREATE KEYSPACE excalibur
    WITH replication = {'class': 'NetworkTopologyStrategy', 'replication_factor' : 3};

DESCRIBE KEYSPACE excalibur;

결과:

CREATE KEYSPACE excalibur WITH replication = {'class': 'NetworkTopologyStrategy', 'DC1': '3', 'DC2': '3'} AND durable_writes = true;

자동 확장 및 데이터센터 재정의 예:

CREATE KEYSPACE excalibur
   WITH replication = {'class': 'NetworkTopologyStrategy', 'replication_factor' : 3, 'DC2': 2};

DESCRIBE KEYSPACE excalibur;

결과:

CREATE KEYSPACE excalibur WITH replication = {'class': 'NetworkTopologyStrategy', 'DC1': '3', 'DC2': '2'} AND durable_writes = true;

replication_factor를 사용하면서 데이터센터를 제외하는 예:

CREATE KEYSPACE excalibur
   WITH replication = {'class': 'NetworkTopologyStrategy', 'replication_factor' : 3, 'DC2': 0};

DESCRIBE KEYSPACE excalibur;

결과:

CREATE KEYSPACE excalibur WITH replication = {'class': 'NetworkTopologyStrategy', 'DC1': '3'} AND durable_writes = true;

일시적 복제(transient replication)가 활성화되면 SimpleStrategy와 NetworkTopologyStrategy 모두에서 '<total_replicas>/<transient_replicas>' 형식으로 복제 계수를 정의해 일시적 리플리카를 구성할 수 있어요.

예를 들어 이 키스페이스는 DC1에 3개의 리플리카(그중 1개 일시적), DC2에 5개의 리플리카(그중 2개 일시적)를 갖습니다:

CREATE KEYSPACE some_keyspace
   WITH replication = {'class': 'NetworkTopologyStrategy', 'DC1' : '3/1', 'DC2' : '5/2'};

USE

USE 문은 현재 키스페이스를 지정된 키스페이스로 변경합니다. CQL의 많은 객체(테이블, 사용자 정의 타입, 함수 등)는 키스페이스에 바인딩되며, 현재 키스페이스는 완전히 자격을 갖춘 이름(키스페이스 이름 접두사 없이)으로 쿼리에서 그 객체들을 참조할 때 사용되는 기본 키스페이스예요. USE 문은 인자로 사용할 키스페이스를 지정합니다:

use_statement::= USE keyspace_name

CQL 사용:

USE excelsior;

ALTER KEYSPACE

ALTER KEYSPACE 문은 키스페이스의 옵션을 수정합니다:

alter_keyspace_statement::= ALTER KEYSPACE [ IF EXISTS ] keyspace_name
	WITH options

예를 들어:

ALTER KEYSPACE excelsior
    WITH replication = {'class': 'SimpleStrategy', 'replication_factor' : 4};

키스페이스가 존재하지 않으면 문은 오류를 반환합니다. 단 IF EXISTS를 사용하면 연산이 no-op이 됩니다. 지원되는 옵션은 키스페이스 생성과 동일합니다.

DROP KEYSPACE

키스페이스 삭제는 DROP KEYSPACE 문으로 합니다:

drop_keyspace_statement::= DROP KEYSPACE [ IF EXISTS ] keyspace_name

예를 들어:

DROP KEYSPACE excelsior;

키스페이스를 삭제하면 모든 테이블, 사용자 정의 타입, 사용자 정의 함수, 그리고 그 테이블에 포함된 모든 데이터를 포함해 그 키스페이스가 즉시, 되돌릴 수 없게 제거됩니다.

키스페이스가 존재하지 않으면 문은 오류를 반환합니다. 단 IF EXISTS를 사용하면 연산이 no-op입니다.

CREATE TABLE

새 테이블 만들기는 CREATE TABLE 문을 사용합니다:

create_table_statement::= CREATE TABLE [ IF NOT EXISTS ] table_name '('
	column_definition  ( ',' column_definition )*
	[ ',' PRIMARY KEY '(' primary_key ')' ]
	 ')' [ WITH table_options ]
column_definition::= column_name cql_type [ STATIC ] [ column_mask ] [ PRIMARY KEY]
column_mask::= MASKED WITH ( DEFAULT | function_name '(' term ( ',' term )* ')' )
primary_key::= partition_key [ ',' clustering_columns ]
partition_key::= column_name  | '(' column_name ( ',' column_name )* ')'
clustering_columns::= column_name ( ',' column_name )*
table_options::= COMPACT STORAGE [ AND table_options ]
	| CLUSTERING ORDER BY '(' clustering_order ')'
	[ AND table_options ]  | options
clustering_order::= column_name (ASC | DESC) ( ',' column_name (ASC | DESC) )*

예를 들어 테이블을 만드는 몇 가지 CQL 문입니다:

CREATE TABLE monkey_species (
    species text PRIMARY KEY,
    common_name text,
    population varint,
    average_size int
) WITH comment='Important biological records';

CREATE TABLE timeline (
    userid uuid,
    posted_month int,
    posted_time uuid,
    body text,
    posted_by text,
    PRIMARY KEY (userid, posted_month, posted_time)
) WITH compaction = { 'class' : 'LeveledCompactionStrategy' };

CREATE TABLE loads (
    machine inet,
    cpu int,
    mtime timeuuid,
    load float,
    PRIMARY KEY ((machine, cpu), mtime)
) WITH CLUSTERING ORDER BY (mtime DESC);

CQL 테이블에는 이름이 있고 일련의 행으로 구성됩니다. 테이블을 만드는 것은 각 행이 가질 컬럼, 그 중 어떤 컬럼이 기본 키를 구성하는지, 그리고 테이블에 대해 정의된 옵션을 정의하는 것과 같아요.

이미 존재하는 테이블을 만들려 하면 IF NOT EXISTS 지시어를 사용하지 않는 한 오류가 반환됩니다. 사용하면 테이블이 이미 존재할 때 문은 no-op이 됩니다.

컬럼 정의

CQL 테이블의 모든 행은 테이블 생성 시 정의된 사전 정의된 컬럼을 갖습니다. 컬럼은 나중에 alter 문으로 추가할 수 있어요.

column_definition은 컬럼의 이름과 타입으로 구성되며, 그 컬럼에 허용되는 값을 제한합니다. 추가로 컬럼 정의는 다음 수식어(modifier)를 가질 수 있습니다:

  • STATIC: 컬럼을 정적 컬럼으로 선언
  • PRIMARY KEY: 컬럼을 테이블 기본 키의 유일한 구성 요소로 선언

정적 컬럼 (Static columns)

일부 컬럼은 테이블 정의에서 STATIC으로 선언될 수 있습니다. 정적 컬럼은 같은 파티션에 속하는(같은 파티션 키를 갖는) 모든 행이 "공유"할 거예요.

예를 들어:

CREATE TABLE t (
    pk int,
    t int,
    v text,
    s text static,
    PRIMARY KEY (pk, t)
);
INSERT INTO t (pk, t, v, s) VALUES (0, 0, 'val0', 'static0');
INSERT INTO t (pk, t, v, s) VALUES (0, 1, 'val1', 'static1');
SELECT * FROM t;
   pk | t | v      | s
  ----+---+--------+-----------
   0  | 0 | 'val0' | 'static1'
   0  | 1 | 'val1' | 'static1'

보시다시피 두 행 모두 파티션(파티션 키가 pk이고 두 행이 같은 파티션에 있음)에서 s 값이 같습니다(static1): 두 번째 삽입이 s 값을 재정의하기 때문이에요.

정적 컬럼의 사용에는 다음 제한이 있습니다:

  • 클러스터링 컬럼이 없는 테이블은 정적 컬럼을 가질 수 없습니다. 클러스터링 컬럼이 없는 테이블에서 각 파티션은 한 행만 가지므로 모든 컬럼이 본질적으로 정적입니다.
  • 기본 키가 아닌 컬럼만 정적일 수 있어요.

기본 키 (Primary key)

테이블 안에서 행은 PRIMARY KEY로 고유하게 식별되므로 모든 테이블은 단일 PRIMARY KEY를 정의해야 합니다. PRIMARY KEY는 테이블의 정의된 컬럼 하나 이상으로 구성됩니다. 문법적으로 기본 키는 PRIMARY KEY 문구 뒤에 괄호 안의 쉼표로 구분된 컬럼 이름 목록으로 정의됩니다. 기본 키가 한 컬럼뿐이면 대신 그 컬럼에 PRIMARY KEY 문구를 추가할 수 있어요. 기본 키 정의의 컬럼 순서가 파티션 키와 클러스터링 컬럼을 정의합니다.

CQL 기본 키는 두 부분으로 구성됩니다:

파티션 키 (partition key)

  • 기본 키 정의의 첫 번째 구성 요소입니다. 단일 컬럼이거나, 추가 괄호 집합을 사용해 여러 컬럼일 수 있어요. 테이블은 최소한 하나의 파티션 키를 가져야 하며, 가능한 가장 작은 테이블 정의는 다음과 같습니다:
CREATE TABLE t (k text PRIMARY KEY);

클러스터링 컬럼 (clustering columns)

  • 기본 키 정의에서 파티션 키를 따르는 컬럼들입니다. 그 컬럼들의 순서가 클러스터링 순서를 정의합니다.

기본 키 정의의 몇 가지 예:

  • PRIMARY KEY (a): a는 단일 파티션 키이고 클러스터링 컬럼은 없음
  • PRIMARY KEY (a, b, c): a는 단일 파티션 키이고 b와 c는 클러스터링 컬럼
  • PRIMARY KEY ((a, b), c): a와 b가 복합 파티션 키를 구성하고 c는 클러스터링 컬럼

기본 키는 위에서 설명한 대로 테이블의 행을 고유하게 식별합니다. 이 고유성의 결과로 같은 기본 키를 사용해 다른 행을 삽입하면 UPSERT가 발생하고 같은 기본 키를 가진 기존 행이 대체됩니다. 기본 키의 일부가 아닌 컬럼은 고유성을 정의할 수 없어요.

파티션 키

테이블 안에서 CQL은 Cassandra 클러스터 내 데이터의 위치를 정의하는 파티션(partition)의 개념을 정의합니다. 파티션은 파티션 키에 대해 같은 값을 공유하는 행의 집합입니다.

파티션 키가 여러 컬럼으로 구성되면, 모든 파티션 키 컬럼에 대해 같은 값을 가질 때 행이 같은 파티션에 속한다는 점에 유의하세요. 파티션 키 컬럼에서 해시가 계산되고 그 해시 값이 파티션 위치를 정의합니다. 예를 들어 다음 테이블 정의와 내용이 주어졌을 때:

CREATE TABLE t (
    a int,
    b int,
    c int,
    d int,
    PRIMARY KEY ((a, b), c, d)
);
INSERT INTO t (a, b, c, d) VALUES (0,0,0,0);
INSERT INTO t (a, b, c, d) VALUES (0,0,1,1);
INSERT INTO t (a, b, c, d) VALUES (0,1,2,2);
INSERT INTO t (a, b, c, d) VALUES (0,1,3,3);
INSERT INTO t (a, b, c, d) VALUES (1,1,4,4);
SELECT * FROM t;

결과:

 a | b | c | d
---+---+---+---
 0 | 0 | 0 | 0 	(1)
 0 | 0 | 1 | 1
 0 | 1 | 2 | 2	(2)
 0 | 1 | 3 | 3
 1 | 1 | 4 | 4  (3)

(5 rows)

1: 행 1과 2는 a와 b 컬럼 모두 0이므로 같은 파티션에 있음. 2: 행 3과 4는 두 행 모두 a가 0이고 b가 1이므로 같은 파티션에 있지만 그 위와는 다른 파티션. 3: 행 5는 a와 b 컬럼 모두 1이므로 혼자서 세 번째 파티션.

테이블은 항상 파티션 키를 가지며, 테이블에 클러스터링 컬럼이 없으면 그 테이블의 각 파티션은 단일 행을 가진다는 점을 유의하세요. 복합이든 아니든 파티션 키가 단일 위치를 식별하기 때문입니다.

파티션의 가장 중요한 속성은 같은 파티션에 속하는 모든 행이 같은 리플리카 노드 집합에 저장되는 것이 보장된다는 점입니다. 즉 테이블의 파티션 키가 클러스터의 같은 노드에 어떤 행이 로컬라이즈될지 정의합니다. 데이터의 로컬라이제이션은 Cassandra 조정자가 가능한 한 적은 노드에 접촉하도록 요구하므로 데이터를 효율적으로 검색하는 데 중요합니다. 하지만 이 보장에는 다른 면이 있어요. 파티션 키를 공유하는 모든 행은 같은 노드에 저장되어 읽기와 쓰기 모두의 핫스팟을 만듭니다. 테이블 행을 그룹화하는 기본 키를 선택하는 것은 배치 업데이트를 돕고 업데이트가 원자적이고 격리된 상태로 이루어지도록 보장할 수 있지만, 파티션은 "너무 크지도 작지도 않은" 크기로 잡아야 합니다.

쿼리 패턴을 고려하고 쿼리에 기반해 기본 키를 할당하는 데이터 모델링이 데이터를 가져올 때 가장 낮은 지연 시간을 갖습니다.

클러스터링 컬럼

테이블의 클러스터링 컬럼은 그 테이블의 파티션에 대한 클러스터링 순서를 정의합니다. 주어진 파티션에 대해 모든 행이 그 클러스터링 순서로 정렬됩니다. 클러스터링 컬럼은 또한 테이블에서 행에 고유성을 추가합니다.

예를 들어:

CREATE TABLE t2 (
    a int,
    b int,
    c int,
    d int,
    PRIMARY KEY (a, b, c)
);
INSERT INTO t2 (a, b, c, d) VALUES (0,0,0,0);
INSERT INTO t2 (a, b, c, d) VALUES (0,0,1,1);
INSERT INTO t2 (a, b, c, d) VALUES (0,1,2,2);
INSERT INTO t2 (a, b, c, d) VALUES (0,1,3,3);
INSERT INTO t2 (a, b, c, d) VALUES (1,1,4,4);
SELECT * FROM t2;

결과:

 a | b | c | d
---+---+---+---
 1 | 1 | 4 | 4	(1)
 0 | 0 | 0 | 0
 0 | 0 | 1 | 1
 0 | 1 | 2 | 2
 0 | 1 | 3 | 3

(5 rows)

1: 행 1은 한 파티션에 있고, 행 2-5는 다른 파티션에 있음. 표시 순서도 다름.

같은 파티션의 네 행을 더 자세히 보면, b 클러스터링 컬럼이 그 행들이 표시되는 순서를 정의합니다. 테이블의 파티션 키가 같은 노드에 행을 그룹화하는 반면, 클러스터링 컬럼은 그 행들이 노드에 어떻게 저장되는지 제어합니다.

이 정렬은 파티션 내 행 범위를 매우 효율적으로 검색하게 해줍니다:

SELECT * FROM t2 WHERE a = 0 AND b > 0 and b <= 3;

결과:

 a | b | c | d
---+---+---+---
 0 | 1 | 2 | 2
 0 | 1 | 3 | 3

(2 rows)

테이블 옵션

CQL 테이블에는 생성 시(그리고 대부분은 나중에 변경해도) 설정할 수 있는 여러 옵션이 있습니다. 이 옵션들은 WITH 키워드 뒤에 지정됩니다.

생성 후에는 변경할 수 없는 중요한 옵션 하나가 있는데, CLUSTERING ORDER BY는 테이블에 대한 쿼리 방식을 영향을 줍니다. 여기서 더 자세히 논의할 가치가 있어요.

클러스터링 순서

테이블의 클러스터링 순서는 클러스터링 컬럼에 의해 정의됩니다. 기본적으로 클러스터링 순서는 클러스터링 컬럼의 데이터 타입에 대해 오름차순입니다. 예를 들어 정수는 1, 2, … n 순서이고, text는 A에서 Z 순서입니다.

CLUSTERING ORDER BY 테이블 옵션은 각각 ASC(오름차순) 또는 DESC(내림차순)로 설정된 클러스터링 컬럼의 쉼표로 구분된 목록을 사용해요. CLUSTERING ORDER BY 옵션이 설정되지 않으면 모든 클러스터링 컬럼에 대해 기본값은 오름차순입니다.

이 옵션은 기본적으로 행을 저장하는 순서를 변경하는 스토리지 엔진에 대한 힌트입니다. 이 옵션을 설정할 때의 결과에 주의하세요:

  • ORDER BY 절이 없는 SELECT 문으로 쿼리할 때 결과의 기본 오름차순이 변경됩니다.
  • 그 테이블의 SELECT 문에서 ORDER BY 절이 사용되는 방식이 제한됩니다. 결과는 원래 클러스터링 순서 또는 역 클러스터링 순서로만 정렬될 수 있어요. 클러스터링 컬럼 a와 b를 WITH CLUSTERING ORDER BY (a DESC, b ASC)로 정의해 테이블을 만들었다고 가정해 보세요. 테이블의 쿼리는 ORDER BY (a DESC, b ASC) 또는 ORDER BY (a ASC, b DESC)를 사용할 수 있습니다. ORDER BY (a ASC, b ASC)ORDER BY (a DESC, b DESC) 같은 혼합 순서는 예상 순서를 반환하지 않아요.
  • 쿼리에 성능 영향을 줍니다. 역 클러스터링 순서의 쿼리는 기본 오름차순보다 느립니다. 주로 내림차순으로 쿼리할 계획이라면 WITH CLUSTERING ORDER BY ()로 테이블 스키마에 클러스터링 순서를 선언하세요. 이 최적화는 가장 새로운 것부터 가장 오래된 것까지 데이터를 검색하는 시계열에서 일반적입니다.

기타 테이블 옵션

테이블은 다음 옵션을 지원합니다:

옵션 종류 기본값 설명
comment simple none 자유 형식의 사람이 읽을 수 있는 주석
speculative_retry simple 99PERCENTILE 추측 재시도 옵션
cdc boolean false 테이블에 Change Data Capture(CDC) 로그 생성
additional_write_policy simple 99PERCENTILE speculative_retry와 동일
gc_grace_seconds simple 864000 툼스톤(삭제 마커)을 가비지 컬렉션하기 전에 기다리는 시간
bloom_filter_fp_chance simple 0.00075 sstable bloom filter의 위양성(false positive) 목표 확률. 해당 bloom filter는 제공된 확률을 제공하도록 크기가 정해지므로, 이 값을 낮추면 bloom filter의 인메모리·온디스크 크기가 영향을 받음
default_time_to_live simple 0 테이블의 기본 만료 시간("TTL") 초
compaction map 아래 참고 컴팩션 옵션
compression map 아래 참고 압축 옵션
caching map 아래 참고 캐싱 옵션
memtable_flush_period_in_ms simple 0 Cassandra가 멤테이블을 디스크로 플러시하기 전의 시간(ms)
read_repair simple BLOCKING 읽기 리페어 동작 설정(아래 참고)

추측 재시도 옵션 (Speculative retry)

기본적으로 Cassandra 읽기 조정자는 일관성 수준을 충족하는 데 필요한 만큼의 리플리카만 쿼리합니다. ONE 일관성 수준에는 하나, QUORUM에는 쿼럼 등을요. speculative_retry는 리플리카가 느리거나 응답하지 않을 때 조정자가 추가 리플리카를 언제 쿼리할지 결정하는데, 이는 리플리카가 느리거나 응답하지 않을 때 유용한 동작입니다. 추측 재시도는 지연 시간을 줄여요. speculative_retry 옵션은 조정자가 일관성 수준을 충족하는 데 필요한 것보다 더 많은 요청을 보내는 신속 읽기 보호(rapid read protection)를 구성합니다.

추가 리플리카에서 자주 읽으면 클러스터 성능을 해칠 수 있습니다. 의심스러우면 기본값인 99PERCENTILE을 유지하세요.

Cassandra 4.0 이전 추측 재시도 정책은 단일 문자열을 파라미터로 받습니다:

  • NONE
  • ALWAYS
  • 99PERCENTILE (PERCENTILE)
  • 50MS (CUSTOM)

추측 재시도 설정의 예는 커스텀 값을 설정합니다:

ALTER TABLE users WITH speculative_retry = '10ms';

이 예는 백분위수를 사용합니다:

ALTER TABLE users WITH speculative_retry = '99PERCENTILE';

백분위수 설정은 역효과를 낼 수 있어요. 단일 호스트가 사용 불가능해지면 백분위수를 끌어올릴 수 있습니다. p99 값은 지정된 백분위수에서 값이 너무 올라갔으므로 의도한 대로 추측하지 않습니다. 일관성 수준이 ALL로 설정되면 추측 재시도 설정과 관계없이 모든 리플리카가 쿼리됩니다.

Cassandra 4.0은 추측 재시도 값의 대소문자 비구분을 지원합니다(CASSANDRA-14293). 예를 들어 값을 none, None, NONE으로 할당하는 것은 같은 효과를 가집니다.

추가로 다음 값이 추가됩니다:

형식 설명
XPERCENTILE 90.5PERCENTILE 조정자가 모든 리플리카의 테이블별 평균 응답 시간을 기록합니다. 리플리카가 이 테이블 평균 응답 시간의 X%보다 오래 걸리면 조정자가 추가 리플리카를 쿼리해요. X는 0과 100 사이여야 합니다.
XP 90.5P XPERCENTILE과 동일
Yms 25ms 리플리카가 응답하는 데 Y 밀리초보다 오래 걸리면 조정자가 추가 리플리카를 쿼리합니다.
MIN(XPERCENTILE,YMS) MIN(99PERCENTILE,35MS) 계산 시점에 낮은 값이 어느 쪽인지에 따라 지정된 백분위수 또는 고정 밀리초를 사용하는 하이브리드 정책. 파라미터는 XPERCENTILE, XP, Yms입니다. 이 설정은 단일 느린 인스턴스로부터 보호하는 데 도움이 됩니다.
MAX(XPERCENTILE,YMS) MAX(90.5P,25ms) 계산 시점에 높은 값이 어느 쪽인지에 따라 지정된 백분위수 또는 고정 밀리초를 사용하는 하이브리드 정책.

Cassandra 4.0은 하이브리드 MIN()MAX() 추측 재시도 정책 지원을 추가하며, MIN(), MAX(), MIN(), MIN() 또는 MAX(), MAX()를 혼합할 수 있어요(CASSANDRA-14293). 하이브리드 모드는 테이블의 정상 p99가 최소값인 50ms 미만이어도 추측합니다. 하지만 p99 수준이 최대값보다 높아지면 그 값이 사용될 수 있습니다. 하이브리드 값에서 한 값은 고정 시간(ms)이어야 하고 다른 하나는 백분위수 값이어야 해요.

변형을 설명하기 위해 다음 예는 모두 유효합니다:

min(99percentile,50ms)
max(99p,50MS)
MAX(99P,50ms)
MIN(99.9PERCENTILE,50ms)
max(90percentile,100MS)
MAX(100.0PERCENTILE,60ms)

additional_write_policy 설정은 저렴한 쿼럼 쓰기가 일시적 리플리카를 포함하도록 업그레이드되는 임계값을 지정합니다.

컴팩션 옵션

컴팩션 옵션은 사용할 컴팩션 전략 클래스를 지정하기 위해 최소한 'class' 하위 옵션을 정의해야 합니다. 지원되는 클래스는:

  • 'SizeTieredCompactionStrategy', STCS(기본값)
  • 'LeveledCompactionStrategy', LCS
  • 'TimeWindowCompactionStrategy', TWCS

커스텀 전략이 필요한 경우 전체 클래스 이름을 문자열 상수로 지정하세요.

기본 전략들은 모두 여러 공통 옵션뿐 아니라 선택한 전략에 특화된 옵션을 지원합니다. 전략에 해당하는 섹션을 참고하세요: STCS, LCS, TWCS.

압축 옵션

압축 옵션은 테이블의 SSTable이 압축되는지, 어떻게 압축되는지 정의합니다. 압축은 CREATE TABLE 또는 ALTER TABLE에 대한 선택 인자로 테이블별로 구성됩니다. 다음 하위 옵션이 있습니다:

옵션 기본값 설명
class LZ4Compressor 사용할 압축 알고리즘. 기본 압축기는 LZ4Compressor, SnappyCompressor, DeflateCompressor, ZstdCompressor입니다. 압축을 끄려면 'enabled' : false를 사용하세요. 커스텀 압축기는 전체 클래스 이름을 문자열 상수로 지정해 제공할 수 있습니다.
enabled true sstable 압축 활성화/비활성화. enabled 옵션이 false로 설정되면 다른 옵션을 지정해서는 안 됩니다.
chunk_length_in_kb 64 온디스크 SSTable은 블록으로 압축됩니다(랜덤 읽기 허용). 이 옵션은 그 블록의 크기(KB)를 정의합니다.
compression_level 3 압축 수준. ZstdCompressor에만 적용됩니다. -131072과 22 사이의 값을 받습니다.

더 큰 값은 압축률을 개선할 수 있지만 읽기 위해 디스크에서 읽어야 하는 데이터의 최소 크기를 늘립니다. 기본값은 테이블 압축에 최적인 값입니다. 압축되지 않은 파일 오프셋에서 청크 번호를 계산할 때 청크 길이는 2의 거듭제곱이어야 해요. 블록 크기는 일반적으로 한 번에 요청되는 데이터 양, 테이블의 평균 행 크기 같은 읽기/쓰기 접근 패턴에 따라 조정될 수 있습니다.

예를 들어 LZ4Compressor와 4 KB의 chunk_length_in_kb로 테이블을 만들려면:

CREATE TABLE simple (
   id int,
   key text,
   value text,
   PRIMARY KEY (key, value)
) WITH compression = {'class': 'LZ4Compressor', 'chunk_length_in_kb': 4};

캐싱 옵션

캐싱은 테이블의 캐시 메모리 사용을 최적화합니다. 캐시된 데이터는 크기와 접근 빈도로 가중치가 매겨져요. 캐싱 옵션은 테이블의 키 캐시와 행 캐시 모두를 구성할 수 있습니다. 다음 하위 옵션이 있습니다:

옵션 기본값 설명
keys ALL 이 테이블의 키(키 캐시)를 캐시할지 여부. 유효한 값은 ALL과 NONE입니다.
rows_per_partition NONE 파티션당 캐시할 행 수(행 캐시). 정수 n을 지정하면 파티션의 처음 n개 조회 행이 캐시됩니다. 유효한 값은 ALL(조회된 파티션의 모든 행 캐시) 또는 NONE(행 캐싱 비활성화)입니다.

예를 들어 키 캐시와 파티션당 10행 캐시를 모두 갖는 테이블을 만들려면:

CREATE TABLE simple (
id int,
key text,
value text,
PRIMARY KEY (key, value)
) WITH caching = {'keys': 'ALL', 'rows_per_partition': 10};

읽기 리페어 옵션

read_repair 옵션은 다양한 성능·일관성 동작을 조정하며 읽기 리페어 동작을 구성합니다.

값은:

옵션 기본값 설명
BLOCKING yes 읽기 리페어가 트리거되면, 읽기가 쓰기가 일관성 수준에 도달할 때까지 다른 리플리카로 보내는 쓰기를 차단합니다.
NONE no 설정하면 조정자가 리플리카 간의 차이를 조정하지만 수리하려 시도하지는 않습니다.

읽기 리페어 동작에 영향을 받는 두 가지 일관성 속성이 있습니다:

  • 단조 쿼럼 읽기(Monotonic quorum reads): 단조 쿼럼 읽기는 어떤 상황에서 읽기가 시간상 뒤로 이동하는 것처럼 보이는 것을 방지합니다. 단조 쿼럼 읽기가 제공되지 않고 쓰기가 리플리카 쿼럼에 도달하지 못하면, 읽은 값이 한 읽기에서는 보이다가 이후 읽기에서는 사라질 수 있어요. BLOCKING이 이 동작을 제공합니다.
  • 쓰기 원자성(Write atomicity): 쓰기 원자성은 읽기가 부분적으로 적용된 쓰기를 반환하는 것을 방지합니다. Cassandra는 파티션 수준 쓰기 원자성을 제공하려 시도하지만, SELECT 문에 포함된 데이터만 읽기 리페어로 수리되므로 데이터를 쓰는 것보다 더 세분화된 수준으로 읽으면 읽기 리페어가 쓰기 원자성을 깨뜨릴 수 있어요. 예를 들어 배치로 클러스터된 파티션에 여러 행을 썼는데 SELECT 문에서 클러스터링 컬럼을 지정해 단일 행을 선택하면 읽기 리페어가 쓰기 원자성을 깨뜨릴 수 있습니다. NONE이 이 동작을 제공합니다.

기타 고려 사항:

  • 새 컬럼 추가(아래 ALTER TABLE 참고)는 상수 시간 연산입니다. 따라서 처음 테이블을 만들 때 미래 사용을 예상할 필요는 없어요.

ALTER TABLE

기존 테이블 변경은 ALTER TABLE 문을 사용합니다:

alter_table_statement::= ALTER TABLE [ IF EXISTS ] table_name alter_table_instruction
alter_table_instruction::= ADD [ IF NOT EXISTS ] column_definition ( ',' column_definition)*
	| DROP [ IF EXISTS ] column_name ( ',' column_name )*
	| RENAME [ IF EXISTS ] column_name to column_name (AND column_name to column_name)*
	| ALTER [ IF EXISTS ] column_name ( column_mask | DROP MASKED )
	| WITH options
column_definition::= column_name cql_type [ column_mask]
column_mask::= MASKED WITH ( DEFAULT | function_name '(' term ( ',' term )* ')' )

테이블이 존재하지 않으면 문은 오류를 반환합니다. 단 IF EXISTS를 사용하면 연산이 no-op입니다.

예를 들어:

ALTER TABLE addamsFamily ADD gravesite varchar;
ALTER TABLE addamsFamily
   WITH comment = 'A most excellent and useful table';

ALTER TABLE 문은 다음을 할 수 있습니다:

  • 테이블에 새 컬럼을 ADD합니다. 테이블의 기본 키는 절대 변경할 수 없어요. 따라서 새 컬럼은 기본 키의 일부가 될 수 없습니다. 컬럼 추가는 테이블 데이터 양에 기반한 상수 시간 연산입니다. 새 컬럼이 이미 존재하면 IF NOT EXISTS를 사용하지 않는 한 문은 오류를 반환하며, 사용하면 연산이 no-op입니다.
  • 테이블에서 컬럼을 DROP합니다. 이 명령은 컬럼과 그 모든 내용을 삭제합니다. 컬럼이 즉시 사용 불가능해지지만 그 내용은 컴팩션 중에 지연(lazily) 제거된다는 점에 유의하세요. 이 지연 제거 때문에 이 명령은 테이블 데이터 양에 기반한 상수 시간 연산입니다. 또한 컬럼을 삭제하면 컬렉션 같은 non-frozen 컬럼이 아니었다면 같은 이름의 컬럼을 다시 추가할 수 있다는 점도 중요합니다. 삭제된 컬럼이 이미 존재하지 않으면 IF EXISTS를 사용하지 않는 한 문은 오류를 반환하며, 사용하면 연산이 no-op입니다.

Warning 컬럼 삭제는 이 컬럼 값에 사용된 타임스탬프가 마이크로초의 "실제" 타임스탬프라고 가정합니다. 마이크로초의 "실제" 타임스탬프를 사용하는 것이 기본이며 강력히 권장되지만, Cassandra는 클라이언트가 어떤 테이블에든 어떤 타임스탬프를 제공할 수 있게 하므로 다른 규약을 사용하는 것이 이론적으로 가능합니다. 그렇게 한다면 컬럼 삭제가 올바르게 실행되지 않을 것임을 유의하세요.

  • 테이블의 기본 키 컬럼을 RENAME합니다. 기본 키가 아닌 컬럼은 이름을 바꿀 수 없습니다. 또한 이미 존재하는 다른 이름으로 컬럼 이름을 바꾸는 것은 허용되지 않아요. 이름을 바꾼 컬럼은 의존하는 이차 인덱스가 없어야 한다는 점을 기억하는 것이 중요합니다. 이름이 바뀐 컬럼이 이미 존재하지 않으면 IF EXISTS를 사용하지 않는 한 문은 오류를 반환하며, 사용하면 연산이 no-op입니다.
  • 테이블 옵션을 변경하려면 WITH를 사용합니다. 지원되는 옵션은 CLUSTERING ORDER를 제외하고 테이블 생성 시 사용된 것과 동일합니다. 다만 어떤 컴팩션 하위 옵션을 설정하면 ALL 이전 컴팩션 옵션이 지워지므로 유지하고 싶은 모든 하위 옵션을 다시 지정해야 해요. 압축 하위 옵션도 마찬가지입니다.

DROP TABLE

테이블 삭제는 DROP TABLE 문을 사용합니다:

drop_table_statement::= DROP TABLE [ IF EXISTS ] table_name

테이블을 삭제하면 포함된 모든 데이터를 포함해 테이블이 즉시, 되돌릴 수 없게 제거됩니다.

테이블이 존재하지 않으면 IF EXISTS를 사용하지 않는 한 문은 오류를 반환하며, 사용하면 연산이 no-op입니다.

TRUNCATE TABLE

테이블은 TRUNCATE 문으로 잘라낼 수 있어요:

truncate_statement::= TRUNCATE [ TABLE ] table_name

TRUNCATE TABLE foo는 다른 DDL 문과의 일관성을 위한 선호 문법입니다. 다만 테이블은 현재 잘라낼 수 있는 유일한 객체이고 TABLE 키워드는 생략할 수 있어요.

테이블을 잘라내면 테이블 자체를 제거하지 않고 테이블의 모든 기존 데이터를 영구적으로 제거합니다.

더 알아보기 (Learn more)