3 minute read

레코드가 기본 키를 통해서만 접근되는 경우, 해당 키에서 파티션을 결정하고 이를 사용하여 해당 키를 담당하는 파티션으로 읽기 및 쓰기 요청을 라우팅할 수 있다.

반면, 보조 인덱스는 일반적으로 레코드를 고유하게 식별하는 것이 아니라 특정 값의 존재 여부를 검색한다.

예를 들어,

  • 사용자 123의 모든 작업 찾기
  • ‘hogwash’라는 단어가 포함된 모든 기사 찾기
  • 색깔이 빨간색인 모든 자동차 찾기

보조 인덱스는 관계형 데이터베이스의 핵심이며, 문서 데이터베이스에서도 흔히 볼 수 있다.

  • 많은 키 값 저장소(예: HBase 및 Voldemort)는 구현 복잡성 때문에 보조 인덱스를 사용하지 않는다.
  • Riak는 데이터 모델링에 매우 유용하기 때문에 보조 인덱스를 사용한다.
  • Solr 및 Elasticsearch와 같은 검색 서버에도 보조 인덱스가 사용된다.

보조 인덱스의 문제점은 파티션에 깔끔하게 매핑되지 않는다는 것이다. 보조 인덱스로 데이터베이스를 분할하는 방법에는 문서 기반 분할과 용어 기반 분할이라는 두 가지 주요 접근 방식이 있다.

보조 인덱스로 데이터베이스 분할하기

문서별로 보조 인덱스를 분할한다

예를 들어 중고차 판매 웹사이트를 운영한다고 가정해 보자.

  • 각 목록에는 고유한 ID(문서 ID라고 함)가 있다.
  • 문서 ID를 기준으로 데이터베이스를 파티션한다
  • 파티션 0의 ID는 0~499, 파티션 1의 ID는 500~999 등

사용자가 자동차를 검색할 때 색상과 제조사별로 필터링할 수 있도록 하려면 색상과 제조사에 대한 보조 인덱스가 필요하다.

예를 들어, 빨간색 자동차가 데이터베이스에 추가될 때마다 데이터베이스 파티션은 인덱스 “항목 색상:빨간색”에 대한 문서 ID 목록에 자동으로 추가한다.

이 인덱싱 방식은 각 파티션을 완전히 분리한다. 각 파티션은 해당 파티션에 있는 문서만 포함하는 자체 보조 인덱스를 메인으로 유지한다. 다른 파티션에 어떤 데이터가 저장되어 있는지는 상관하지 않는다. 문서를 추가, 제거 또는 업데이트하는 등 데이터베이스에 기록해야 할 때마다 기록하려는 문서 ID가 포함된 파티션만 처리하면 된다. 이러한 이유로 문서 분할 인덱스를 로컬 인덱스라고도 한다.

문서 분할 인덱스에서 읽을 때는 주의가 필요하다. 문서 ID에 특별한 작업을 하지 않는 한 특정 색상이나 특정 제조사의 자동차가 모두 같은 파티션에 있을 이유가 없기 때문이다.

분할된 데이터베이스를 쿼리하는 이러한 접근 방식을 분산/집합이라고도 하며, 보조 인덱스에 대한 읽기 쿼리의 비용이 상당히 높아질 수 있다.

파티션을 병렬로 쿼리하는 경우에도 분산/수집은 꼬리 지연 시간이 증폭되기 쉽다. 그럼에도 불구하고 널리 사용되고 있다. 예를 들어, MongoDB, Riak, Cassandra, Elasticsearch, SolrCloud, VoltDB 는 모두 문서 분할 보조 인덱스를 사용한다. 대부분의 데이터베이스는 보조 인덱스 쿼리가 단일 파티션에서 제공될 수 있도록 파티셔닝 체계를 구성할 것을 권장하지만, 특히 단일 쿼리에서 여러 보조 인덱스를 사용하는 경우(예: 색상 및 제조사별로 자동차를 동시에 필터링하는 경우)에는 이것이 항상 가능한 것은 아니다.

용어별로 보조 인덱스 분할

각 파티션마다 고유한 보조 인덱스(로컬 인덱스)를 갖는 대신 모든 파티션의 데이터를 포괄하는 글로벌 인덱스를 구성할 수 있다. 하지만 이 인덱스를 하나의 노드에만 저장할 수는 없다. 병목 현상이 발생하여 파티셔닝의 취지를 무색하게 할 수 있다. 전역 인덱스도 파티셔닝해야 하지만 기본 키 인덱스와는 다른 방식으로 파티셔닝할 수 있다.

모든 파티션의 빨간색 자동차가 인덱스에서 color:red 아래에 나타나지만, 문자 a부터 r까지로 시작하는 색상은 파티션 0에 나타나고 s부터 z까지로 시작하는 색상은 파티션 1에 나타나도록 인덱스가 분할되어 있는 모습을 보여 준다. 자동차 제조사에 대한 인덱스도 이와 유사하게 분할된다.

이러한 종류의 인덱스를 용어 분할 인덱스라고 부르는데, 찾고자 하는 용어가 인덱스의 분할을 억제하기 때문이다. 용어(Term)이라는 이름은 전체 텍스트 인덱스(특정 종류의 보조 인덱스)에서 유래하며, 여기서 용어는 문서에 나타나는 모든 단어다. 이전과 마찬가지로 용어 자체 또는 용어의 해시를 사용하여 인덱스를 분할할 수 있다. 용어 자체로 분할하는 것은 범위 스캔(예: 자동차 판매 가격과 같은 숫자 속성)에 유용할 수 있는 반면, 용어의 해시를 기준으로 분할하면 부하를 더 고르게 분산시킬 수 있다.

문서 분할 인덱스에 비해 전역(용어 분할) 인덱스의 장점은 읽기 효율을 높일 수 있다는 것이다. 클라이언트는 모든 파티션에 대해 분산/수집을 수행하는 대신 원하는 용어가 포함된 파티션에 대해서만 요청하면 된다. 그러나 전역 인덱스의 단점은 단일 문서에 대한 쓰기가 인덱스의 여러 파티션에 영향을 미칠 수 있기 때문에 쓰기 속도가 느리고 복잡하다는 것이다. 예를 들어, 문서의 모든 용어가 다른 파티션, 다른 노드에 있을 수 있다.

이상적인 세계에서는 인덱스가 항상 최신 상태로 유지되고 데이터베이스에 기록된 모든 문서가 인덱스에 즉시 반영된다. 그러나 기간 분할 인덱스가 최신 상태로 유지되기 위해서는 쓰기의 영향을 받는 모든 파티션에 걸쳐 분산 트랜잭션이 필요하며, 이는 모든 데이터베이스에서 지원되지 않는다.

실제로 글로벌 보조 인덱스에 대한 업데이트는 종종 비동기식이다. 즉, 쓰기 직후 인덱스를 읽는 경우 방금 변경한 내용이 아직 인덱스에 반영되지 않을 수 있다. 예를 들어, Amazon DynamoDB는 글로벌 보조 인덱스가 정상적인 상황에서는 몇 분의 1초 이내에 업데이트되지만 인프라에 결함이 있는 경우 전파 지연이 더 길어질 수 있다.

글로벌 용어 분할 인덱스의 다른 용도로는 Riak의 검색 기능과 로컬 인덱싱과 글로벌 인덱싱 중에서 선택할 수 있는 Oracle 데이터 웨어하우스가 있다.