DBMS/Cassandra

Cassandra Secondary Index

반응형

Cassandra Secondary Index

Cassandra의 테이블 모델링 방식은 Query-Driven Modeling을 기반으로 하여 데이터 액세스 패턴에 맞춰 설계됩니다. 그러나 이러한 방식은 유연성이 부족하다는 단점이 있습니다.

대안으로 원본 테이블과 별도로 자체 인덱스의 역할을 하는 테이블을 생성해서 배치로 저장하는 형태로 관리 중입니다.

  • 테이블을 N벌로 관리해야하고 특히나 운영 과정 중 인덱스 테이블을 추가하려면 전체 데이터 마이그레이션이 필요한 문제점이 존재한 상황.

1. 앞서서...


image

공식문서에서는 세컨더리 인덱스 사용의 경고를 확인할 수 있고, 이외의.. 구글링시 세컨더리 인덱스를 사용하지 말라는 등등의 블로그 자료들이 많이 확인되었습니다.


2. 세컨더리 인덱스를 정말로 사용하면 안 될까요?


문제 1: 세컨더리 인덱스를 사용하면 항상 모든 노드에 부하가 발생할까?

카산드라의 세컨더리 인덱스는 로컬 인덱스(Local Index)로 구성됩니다.

  • 로컬 인덱스는 해당 데이터를 보유한 노드의 로컬에만 존재하는 인덱스입니다.
  • 이로 인해 특정 데이터를 가진 노드를 사전에 알 수 없으며, 최악의 경우 모든 노드에 쿼리를 요청한 후 결과를 조합해야 하는 상황이 발생할 수 있습니다.

하지만 파티션 키를 지정한 상태에서 조회하는 경우에는 부하 분산이 가능할 것으로 예상되어 간단한 테스트를 진행해 보았습니다.

  • 그 결과 파티션 키를 지정하고 세컨더리 인덱스를 사용하면 해당 노드로만 요청이 전달됨을 확인할 수 있었습니다. (하단 테스트 내용 참고)

테이블 구조

create table test
(
    partition text,
    sort bigint,
    status text,
    post_id text,
    primary key ( (partition), sort )
) with clustering order by (sort desc);

테스트 데이터

insert into test(partition, sort, status, post_id) values('aaa', 7, 'ACTIVE', '7');
insert into test(partition, sort, status, post_id) values('aaa', 8, 'ACTIVE', '8');
insert into test(partition, sort, status, post_id) values('aaa', 9, 'DELETE', 'd9');
...
select * from test where partition = 'aaa' and sort = 8 //  가능
select * from test where partition = 'aaa' and sort = 8  and status = 'ACTIVE'; // 기본적으로 불가능 

select * from test where partition = 'aaa' and sort < 8 //  가능
select * from test where partition = 'aaa' and sort < 8  and status = 'ACTIVE'; // 기본적으로 불가능

(불가능의 경우 다음과 같은 에러 발생함)

Cannot execute this query as it might involve data filtering and thus may have unpredictable performance

Case 1. 세컨더리 인덱스로 조회 (파티션 키 지정 X)

select * from test where status = 'ACTIVE';
image image image
  • 길이만 봐도 알 수 있듯이, 모든 노드(4개)에 요청을 보내고, 각각 세컨더리 인덱스를 읽어서 SSTable로 부터 데이터를 조회.

 

Case 2. 세컨더리 인덱스로 조회 (파티션 키 지정 O, 클러스터링 키 지정 O)

select * from test where partition = 'aaa' and sort < 8  and status = 'ACTIVE';
image
  • 1개 노드만을 대상으로 Secondary Index를 조회하고, 해당 인덱스를 이용해서 SSTable로 부터 데이터를 조회.

 

Case 3. 세컨더리 인덱스로 조회 (파티션 키 지정 O, 클러스터링 키 지정 X)

select * from test where partition = 'aaa' and status = 'ACTIVE';
image
  • 동일하게 1개 노드만을 대상으로 Secondary Index를 조회하고, 해당 인덱스를 이용해서 SSTable로 부터 데이터를 조회.

검증한 내용

  • 세컨더리 인덱스로 데이터를 읽을때, 파티션 키를 지정하지 않으면 모든 노드로 브로드캐스트 형태로 요청이 보내진다.
  • 다만 파티션을 지정하고 세컨더리 인덱스로 데이터를 읽는 경우, 해당 노드로만 요청이 보내진다.

-> 서비스에서 사용할 쿼리에 대해서, 파티션을 지정하고 세컨더리 인덱스로 쿼리를 처리할 수 있지 않을까 생각됨.


문제 2: 세컨더리 인덱스를 사용하지 말아야 하는 경우

참고 문서

다음과 같은 경우에는 세컨더리 인덱스 사용을 지양하는 것이 좋습니다.

  • 높은 카디널리티(High Cardinality)를 가진 컬럼
    • 카디널리티가 높은 컬럼(예: 유저 ID, 타임스탬프 등)은 각 값이 거의 유일하게 분포되어 있기 때문에, 인덱스 검색 효율이 떨어지고 조회 시 부하가 증가할 수 있음. (카디널리티가 매우 낮은 컬럼도 사용하면 위험함)
    • 가능성 (생각): 카테고리 같이 카디널리티가 적당한 컬럼에 대해서는 제한적으로 활용할 수 있지 않을까 싶음.
  • 카운터(Counter) 컬럼
  • 수정과 삭제가 자주 발생하는 컬럼
    • 세컨더리 인덱스는 데이터 변경 시마다 자동으로 업데이트되어야 하므로, 변경이 빈번한 컬럼에서는 성능 저하가 발생할 가능성이 큼.
    • 가능성 (생각): 인덱스 비용으로 당연한 내용
  • 넓은 범위(Range)의 쿼리
    • 세컨더리 인덱스는 넓은 범위를 대상으로 검색할 경우 비효율적일 수 있음.
    • 가능성 (생각): 파티션 키와 클러스터링 키를 활용하여 검색 범위를 충분히 좁혀둔 상태에서 추가 필터링 용도로 활용하는 것은 괜찮을 수도 있음.


3. 개선되고 있는 카산드라 세컨더리 인덱스


카산드라의 세컨더리 인덱스는 지속적으로 개선되고 있으며, 현재까지 주요한 인덱싱 방식은 다음과 같습니다.

  1. Secondary Indexing (모든 버전에서 사용 가능)
    • 카산드라의 기본적인 세컨더리 인덱스 방식.
    • 개별 노드의 로컬 범위에서만 동작.
      • 특정 데이터를 가진 노드를 사전에 알 수 없어서, 파티션 키 없이 사용하면 전체 노드에 부하가 발생할 가능성이 있음.
  2. SASI Index (Cassandra 3.6+)
    • SSTable Attached Secondary Index 공식 문서
    • 특징:
      • 기존 세컨더리 인덱스와 동일하게 로컬 범위의 인덱스로 구성됨.
      • SASI 인덱스는 memtable에서 데이터를 모은 후, SSTable로 flush될 때 인덱스가 함께 생성됨.
        • 기존 세컨더리 인덱스는 별도의 조회 테이블을 생성하는 방식, 반면 SASI는 SSTable 내부에 인덱스를 함께 저장.
        • 디스크 사용량 감소 및 별도의 memtable, bloom filter, 파티션 인덱스가 필요 없음 → 오버헤드 최소화.
        • 기존 세컨더리 인덱스는 =(동등 비교)만 지원했으나, 전방 일치 (LIKE 'prefix%') 등의 검색 기능이 추가됨.
        • 다만, SASIS 인덱스는 실험적 기능으로, 프로덕션 사용을 권장하지 않음.
      • 기본적으로 비활성화되어 있으며, 설정을 변경해야 사용 가능.
      • Cassandra 5.0에 등장한 SAI(Indexing)의 초기 실험 기능으로 보임.
  3. SAI Index (Cassandra 5.0+)
    • Storage-Attached Indexing 공식 문서
    • 특징
      • 기존 세컨더리 인덱스와 동일하게 로컬 범위의 인덱스로 구성됨.
      • SASI.Index를 개선한 버전으로, SASI를 대체하는 역할.
      • 주요 개선 사항:
        • Composite Partition Key 중 하나의 컬럼에 대해 인덱스 생성 가능 (단, 단일 컬럼으로 이루어진 파티션 키에는 사용 불가능.)
        • Collection 타입 지원
        • Numeric Range 및 IN 절 필터링 지원
      • 실험적 기능이라는 별도 언급이 없어, 프로덕션 사용 가능성이 높아 보임. (추가 확인 필요)


4. 세컨더리 인덱스 사용 시 기대 효과 및 문제점


예상되는 문제점

  • 잘못 사용할 경우 전체 노드에 부하를 유발할 가능성
    • 세컨더리 인덱스를 추가하면 해당 컬럼만으로도 쿼리가 가능해지므로, 파티션 키 없이 사용하면 전체 노드로 부하가 확산될 위험이 있음.
    • 일반적인 상황에서는 사용하지 않고, 상태 값 필터링 등 제한적인 케이스에서만 활용해야 함.

기대 효과

  • 상태 값/카테고리 필터링 등 기능 강화 → Cassandra 기반 서비스의 유연성 개선


5. 남은 검토 내용들


운영 중 인덱스 추가

  • 신규 테이블의 경우 이슈 없으나 서비스 중인 테이블에 세컨더리 인덱스 추가가 가능할지.
    • 기존 운영중인 테이블에 세컨더리 인덱스 생성 후 nodetool rebuild index로 인덱스 빌드 가능. 메뉴얼
    • 서비스 영향도
      • 세컨더리 인덱스 사용시, 일반 쿼리의 경우 블로킹 되지 않는다 함
      • 다만, rebuild index 시에도 영향도가 없을지 검증은 필요해보임.

Write & Read Performance 영향도

  • 인덱스 추가시 성능 비교 필요

인덱스 크기에 따른 영향도

  • 인덱스 크기가 커졌을때의 영향도는 없을지 검토 필요.


6. 마치며..


  • 현재 검토가 필요한 단계이지만, 세컨더리 인덱스를 활용해서 카산드라의 유연성 부족 문제를 일부 해소할 수 있을 것으로 기대됨.
  • 특히, SAI Index(Cassandra 5.0~)가 안정화된다면 기존 세컨더리 인덱스의 문제점을 상당 부분 개선할 수 있을 것으로 보임.
  • 다만 잘못 사용했을 경우 안정성 문제가 존재해서, 활용시 안정성 보완은 필요해보임.
반응형