TechnologyJune 8, 2015

CQL WHERE 절 자세히 살펴보기

CQL WHERE 절 자세히 살펴보기

이 블로그에서 소개한 CQL 문을 직접 실습해보고 싶다면 Cassandra 기본 개념 학습 시리즈의 쿼리 부분을 참고하세요.

 


 

CQL과 SQL에는 유사한 구문이 존재하지만 차이점도 상당히 많습니다. 차이점이 발생하는 주요 원인은 Cassandra는 분산 데이터를 다루며 비효율적인 쿼리를 방지하는 것을 목표로 삼기 때문입니다. 특히 WHERE 절에서 CQL과 SQL의 차이가 두드러집니다. 이번 포스트의 목적은 CQL WHERE 절은 어떤 대상을 지원하며 해당 절이 일반 SQL과 다른 이유를 알아보는 것입니다.

주 키 열

Cassandra에서 특별한 역할을 수행하는 열은 두 가지가 있습니다. 바로 파티션 키 열과 클러스터링 열입니다. 두 가지는 함께  주 키. 행을 정의합니다. 파티션 키 열은 주 키의 첫 번째 부분이며 클러스터에 데이터를 균등하게 분산하는 역할을 합니다. 행은 파티션 키의 해시 에 기반하여 클러스터에 분산됩니다. 클러스터링 키 열을 통해 파티션의 데이터를 클러스터링하는 데 사용되어 행을 효과적으로 검색할 수 있습니다. 파티션 키, 클러스터링 열, 일반 열은 역할이 서로 다른 만큼 WHERE 절 내에서 서로 다른 제한 조건들을 지원합니다. 뿐만 아니라 각각의 제한 조건은 SELECT, UPDATE 또는 DELETE와 같은 쿼리의 종류에 따라서도 달라집니다.

SELECT 문의 WHERE 절 제한 조건

파티션 키 제한 조건

파티션 키 열은 =와 IN 두 개의 연산자만을 지원합니다.

IN 제한 조건

2.2 이전에는 IN 제한 조건이 파티션 키의 마지막 열에만 적용될 수 있었습니다. 예를 들면 다음과 같은 테이블이 있다고 가정해보겠습니다.

CREATE TABLE numberOfRequests ( cluster text, date text, time text, numberOfRequests int, PRIMARY KEY ((cluster, date), time) )

2.1에서는 date 열에서만 IN 연산자를 사용할 수 있습니다. 2.2에서는 어떤 파티션 키 열. 에서든 IN 연산자를 사용할 수 있습니다. 따라서 다음과 같은 쿼리가 있다고 가정해 봅시다.

SELECT * FROM numberOfRequests WHERE cluster IN ('cluster1', 'cluster2') AND date = '2015-05-06' AND time >= '12:00' AND time <= '14:00';

이는 2.2 이후로는 유효하지만 그 전에는 유효하지 않았던 쿼리입니다. 이러한 변화는 CQL의 일관성을 높여줬습니다. 하지만 파티션 키 열에서 IN 제한 조건을 사용할 때는 여전히 주의하는 것이 좋습니다. Ryan Svihla가 작성한 포스트 를 읽어보면 IN 연산자 사용을 지양해야 하는 이유를 명확히 알 수 있습니다. 2.2의 또 다른 변경 사항은 더 이상 IN 절에서 지정된 파티션 키의 순서대로 결과가 반환되지 않는다는 것입니다. 2.2부터 결과는 열 유형의 원래 순서에 따라 반환되며 복제된 부분은 무시됩니다.

 

제한 조건 없는 파티션 키 열

Cassandra에서는 보조 인덱스를 사용하지 않는 한 파티션 키 열을 모두 함께 제한하거나 제한하지 않는 것만 가능합니다. 그에 따라 다음과 같은 쿼리가 있다고 가정해 봅시다.

SELECT * FROM numberOfRequests WHERE cluster='cluster1' AND time ='12:00';

date 열이 제한되지 않았으므로 이 쿼리는 거부될 것입니다. Cassandra는 모든 파티션 키 열이 있어야 파티션을 포함하는 노드를 찾게 해주는 해시를 계산할 수 있기 때문입니다. 모든 파티션 키에 제한을 부여하지 않지만 클러스터링 키 일부에는 제한을 부여하는 경우, Cassandra는 ALLOW FILTERING을 쿼리에 추가하라고 요구합니다. ALLOW FILTERING에 대한 자세한 내용은 ALLOW FILTERING에 대하여 를 읽어보시기 바랍니다.

 

연산자 >, >=, <=, <

Cassandra는 선택된 파티셔녀 를 사용해서 파티션을 노드로 분배합니다. ByteOrderedPartitioner만이 데이터의 분산 순서를 유지하므로 Cassandra는 연산자 >, >=, <=, <를 파티션 키에서 직접적으로 지원하지 않습니다. 대신 토큰 함수를 사용하면 연산자 >, >=, <=, <를 사용할 수 있습니다.

SELECT * FROM numberOfRequests WHERE token(cluster, date) > token('cluster1', '2015-06-03') AND token(cluster, date) <= token('cluster1', '2015-06-05') AND time = '12:00';

ByteOrderedPartitioner를 사용하면 복수의 파티션에서 범위 쿼리를 수행할 수 있습니다. 하지만 이때 주의를 기울여야 합니다. ByteOrderedPartitioner는 클러스터 간의 불균형을 야기할 수 있으므로 사용을 권장하지 않습니다.

 

클러스터링 열 제한

클러스터링 열은 하나의 열에서 =, IN, >, >=, <=, <, CONTAINS, CONTAINS KEY 연산자를 통한 제한을 지원하며 복수의 열에서는 =, IN, >, >=, <=, <,을 사용한 제한을 지원합니다.

제한되지 않는 클러스터링 열

클러스터링 열의 역할은 파티션 내에서 데이터를 클러스터링하는 것입니다. 다음과 같은 테이블이 있다고 가정해 봅시다.

CREATE TABLE numberOfRequests ( cluster text, date text, datacenter text, hour int, minute int, numberOfRequests int, PRIMARY KEY ((cluster, date), datacenter, hour, minute))

이 경우 데이터는 각 파티션에서 다음과 같이 저장됩니다.

 

{datacenter: US_WEST_COAST {hour: 0 {minute: 0 {numberOfRequests: 130}} {minute: 1 {numberOfRequests: 125}} … {minute: 59 {numberOfRequests: 97}}} {hour: 1 {minute: 0 …

보조 인덱스 없이 데이터를 효과적으로 검색하고 싶다면 선택한 모든 클러스터링 키 열을 알아야 한다는 사실을 알 수 있습니다. 따라서 다음을 실행한다고 가정해 봅시다.

SELECT * FROM numberOfRequests WHERE cluster = ‘cluster1’ AND date = ‘2015-06-05’ AND datacenter = 'US_WEST_COAST' AND hour = 14 AND minute = 00;

이때 Cassandra는 데이터를 효율적으로 찾아낼 것입니다. 하지만 다음을 실행한다고 가정해 봅시다.

SELECT * FROM numberOfRequests WHERE cluster = ‘cluster1’ AND date = ‘2015-06-05’ AND hour = 14 AND minute = 0;

이때 Cassandra는 요청된 데이터를 찾기 위해 파티션 전체를 스캔해야 하므로 쿼리를 거부합니다.

 

IN 제한 조건

2.2 전에는 클러스터링 열에서의 IN 제한 조건이 마지막 클러스터링 열에서만 허용되었습니다. 2.2에서는 어떤 열에서든 IN 제한 조건이 허용되면서 다음과 같은 쿼리를 수행할 수 있게 되었습니다.

SELECT * FROM numberOfRequests WHERE cluster = ‘cluster1’ AND date = ‘2015-06-05’ AND datacenter = 'US_WEST_COAST' AND hour IN (14, 15) AND minute = 0;

2.2 전에는 다중 열 IN 제한 조건 을 사용해서 위와 동일한 결과의 데이터를 검색할 수 있었습니다.

SELECT * FROM numberOfRequests WHERE cluster = ‘cluster1’ AND date = ‘2015-06-05’ AND datacenter = 'US_WEST_COAST' AND (hour, minute) IN ((14, 0), (15, 0));

2.2에서 다중 열 IN 제한 조건은 모든 클러스터링 열 집합에 적용할 수 있습니다.

SELECT * FROM numberOfRequests WHERE cluster = ‘cluster1’ AND date = ‘2015-06-05’ AND (datacentre, hour) IN (('US_WEST_COAST', 14), (‘US_EAST_COAST’, 17)) AND minute = 0;

2.2 전에는 멀티 열 IN 제한 조건을 사용해 제한할 수 있는 것은 클러스터링 열의 마지막 집합뿐이었습니다. 그래서 위의 쿼리는 2.1에서는 유효하지 않았습니다. 하지만 다음과 같은 쿼리는 유효했습니다.

SELECT * FROM numberOfRequests WHERE cluster = ‘cluster1’ AND date = ‘2015-06-05’ AND datacenter = 'US_WEST_COAST' AND (hour) IN ((14), (15));

 

>, >=, <=, < 제한 조건

단일 열 슬라이스 제한 조건을 사용해 제한할 수 있는 것은 클러스터링 열의 마지막 집합뿐입니다. 따라서 다음과 같은 쿼리는 유효하지 않습니다.

SELECT * FROM numberOfRequests WHERE cluster = ‘cluster1’ AND date = ‘2015-06-05’ AND datacenter = 'US_WEST_COAST' AND hour= 12 AND minute >= 0 AND minute <= 30; SELECT * FROM numberOfRequests WHERE cluster = ‘cluster1’ AND date = ‘2015-06-05’ AND datacenter = 'US_WEST_COAST' AND hour >= 12; SELECT * FROM numberOfRequests WHERE cluster = ‘cluster1’ AND date = ‘2015-06-05’ AND datacenter > 'US';

하지만 다음과 같은 쿼리는 유효합니다.

SELECT * FROM numberOfRequests WHERE cluster = ‘cluster1’ AND date = ‘2015-06-05’ AND datacenter = 'US_WEST_COAST' AND hour >= 12 AND minute = 0;

멀티 열 슬라이스 제한 조건을 사용해서 클러스터링 열의 마지막 집합을 제한할 수 있습니다.

SELECT * FROM numberOfRequests WHERE cluster = ‘cluster1’ AND date = ‘2015-06-05’ AND datacenter = 'US_WESTCOAST' AND (hour, minute) >= (12, 0) AND (hour, minute) <= (14, 0)

양쪽이 모두 슬라이스로 지정된다면 제한 조건은 반드시 동일한 열로 시작해야 합니다. 따라서 다음과 같은 쿼리는 유효합니다.

SELECT * FROM numberOfRequests WHERE cluster = ‘cluster1’ AND date = ‘2015-06-05’ AND datacenter = 'US_WEST_COAST' AND (hour, minute) >= (12, 30) AND (hour) < (14)

하지만 다음은 유효하지 않습니다.

SELECT * FROM numberOfRequests WHERE cluster = ‘cluster1’ AND date = ‘2015-06-05’ AND datacentre = 'US_WEST_COAST' AND (hour, minute) >= (12, 0) AND (minute) <= (45)

 

CONTAINS와 CONTAINS KEY 제한 조건

CONTAINS와 CONTAINS KEY 제한 조건은 쿼리가 보조 인덱스를 사용할 때만 컬렉션에서 사용할 수 있습니다.

일반 열 제한 조건

일반 열은 쿼리가 보조 인덱스 쿼리일 때 =, >, >=, <= 및 <, CONTAINS 또는 CONTAINS KEY 제한 조건에 의해 제한될 수 있습니다. IN 제한 조건은 지원되지 않습니다.

보조 인덱스 쿼리

보조 인덱스에 대한 직접 쿼리는 =, CONTAINS 또는 CONTAINS KEY 제한 조건만을 지원합니다. CONTAINS 제한 조건은 컬렉션 유형에만 사용할 수 있습니다. CONTAINS KEY 제한 조건은 키가 인덱싱된 맵에서만 사용할 수 있습니다. 예를 들어 다음과 같은 테이블이 있다고 가정해 봅시다.

CREATE TABLE contacts ( id int PRIMARY KEY, firstName text, lastName text, phones map<text, text="">, emails set ); CREATE INDEX ON contacts (firstName); CREATE INDEX ON contacts (keys(phones)); // Using the keys function to index the map keys CREATE INDEX ON contacts (emails); </text,>

이때 다음과 같은 쿼리가 유효합니다.

SELECT * FROM contacts WHERE firstname = 'Benjamin'; SELECT * FROM contacts WHERE phones CONTAINS KEY 'office'; SELECT * FROM contacts WHERE emails CONTAINS 'Benjamin@oops.com';

 

보조 인덱스 필터링

보조 인덱스 쿼리는 필터링을 사용해 인덱싱되지 않은 열을 =, >, >=, <=와 <, CONTAINS와 CONTAINS KEY 제한 조건으로 제한함으로써 반환 결과를 제한할 수 있게 해줍니다. 따라서 ALLOW FITERING을 지정한다면 다음과 같은 쿼리는 유효합니다.

SELECT * FROM contacts WHERE firstname = 'Benjamin' AND lastname = 'Lerer' ALLOW FILTERING; SELECT * FROM contacts WHERE phones CONTAINS KEY 'office' AND phones CONTAINS '0000.0000.0000' ALLOW FILTERING;

하지만 필터링은 주의해서 사용해야 합니다. 이는 비싼 연산이 될 수 있기 때문입니다.

 

파티션 키 제한 조건과 보조 인덱스

Cassandra는 보조 인덱스 쿼리를 수행해야 하는 경우 모든 노드에 접속해서 각 노드에 위치한 보조 인덱스의 조각을 확인합니다. 모든 파티션 키 구성 요소가 제한되어 있다면 Cassandra는 해당 정보를 사용해 지정된 파티션 키가 존재하는 노드만을 쿼리함으로써 쿼리의 효율성을 높입니다. 보조 인덱스 쿼리에서는 파티션 키 열에 = 제한 조건만을 사용할 수 있습니다.

클러스터링 열 제한 조건과 보조 인덱스

Cassandra는 인덱싱된 각 값에 대하여 해당 값을 포함하고 있는 각 행의 주 키 전체(파티션 키 열 + 클러스터링 열)를 저장합니다. Cassandra는 인덱스 쿼리를 수행하는 경우 인덱스의 값을 포함하는 행의 주 키를 검색합니다. 그 후 테이블에서 열을 검색한 뒤 그에 대하여 필요한 필터링을 수행합니다. 첫 클러스터링 열이 제한된 경우 Cassandra는 인덱스에 의해 반환된 주 키에 대해 조기 필터링을 수행하여 필터링의 효율성을 높입니다. 해당 유형의 필터링에서 Cassandra는 다음과 같은 클러스터링 열 제한 조건을 수용합니다. =, IN, >, >=, <=, <. 이때 numberOfRequests 테이블에 다음과 같은 보조 인덱스를 추가한다고 가정해 봅시다.

CREATE INDEX ON numberOfRequests (minute);

그러면 다음과 같은 쿼리는 유효합니다.

SELECT * FROM numberOfRequests WHERE cluster = 'cluster1' AND date = '2015-06-05' AND datacenter IN ('US_WEST_COAST', 'US_EAST_COAST') AND minute = 0 ALLOW FILTERING;

 

UPDATE와 DELETE 문에 대한 WHERE 절 제한 조건

UPDATE와 DELETE 문에서는 모든 주 키 열을 제한해야 하며 다음과 같은 제한 조건만 허용됩니다.

  • 임의의 파티션 키 또는 클러스터링 열에 대한 단일 열 =
  • 마지막 파티션 키 열에 대한 단일 열 IN 제한 조건

CASSANDRA-6237 을 통해 3.0에서 UPDATE 및 DELETE 문에 대해 다음과 같은 지원을 추가하여 위와 같은 제한 사항이 일부 완화됩니다.

  • 임의의 파티션 키 열에 대한 IN 제한 조건
  • 임의의 클러스터링 열에 대한 IN 제한 조건
  • 클러스터링 키에 대한 EQ와 IN 다중 열 제한 조건(단일 열 제한 조건과 혼합되거나 혼합되지 않을 수 있음)

또한 전체 행의 범위 삭제에 대한 DELETE 문 지원이 강화됩니다. 조건부 업데이트 또는 삭제에 대한 IN 제한 조건은 아직 지원되지 않을 예정입니다. UPDATE와 DELETE 문에 대한 보조 인덱스 검색은 쓰기 전에 읽기가 위험하기 때문에 현재 지원되지 않으며 앞으로도 지원되지 않을 것입니다.

결론

Cassandra를 최대한 활용하기 위해서는 수행하고자 하는 쿼리에 맞게 테이블을 설계해야 합니다. 이 포스트에서 설명한 내용이 테이블 설계에 도움이 되길 바랍니다.

단일 및 다중 열 제한 조건 조합에 대한 마지막 확인 사항

Cassandra의 일부 버전에서는 단일 및 다중 열 제한 조건을 사용할 수 없었습니다. 이 문제는 2.0.15와 2.1.5에서 모두 해결되었습니다.

 

Share

One-stop Data API for Production GenAI

Astra DB gives JavaScript developers a complete data API and out-of-the-box integrations that make it easier to build production RAG apps with high relevancy and low latency.