검색 색인

이 페이지에서는 검색 색인을 추가하는 방법을 설명합니다. 전체 텍스트 검색은 검색 색인의 항목에 대해 실행됩니다.

검색 색인 사용 방법

전체 텍스트 검색에 사용할 수 있도록 모든 열에 검색 색인을 만들 수 있습니다. 검색 색인을 만들려면 CREATE SEARCH INDEX DDL 문을 사용합니다. 색인을 업데이트하려면 ALTER SEARCH INDEX DDL 문을 사용합니다. Spanner는 데이터베이스에서 데이터가 변경되는 즉시 검색 색인에서 데이터를 추가 및 업데이트하는 등 검색 색인을 자동으로 빌드하고 유지보수합니다.

검색 색인 파티션

검색 색인은 가속하려는 쿼리 유형에 따라 파티션을 나눈 또는 파티션을 나누지 않은 색인일 수 있습니다.

  • 파티션을 나눈 색인이 가장 적합한 예는 애플리케이션이 이메일 편지함을 쿼리하는 경우입니다. 각 쿼리가 특정 편지함으로 제한됩니다.

  • 파티션을 나누지 않은 쿼리가 가장 적합한 예는 제품 카탈로그의 모든 제품 카테고리에 대한 쿼리가 있는 경우입니다.

검색 색인 사용 사례

전체 텍스트 검색 외에도 Spanner 검색 색인은 다음을 지원합니다.

  • 하위 문자열 검색: 더 긴 텍스트 본문 내에서 더 짧은 문자열(하위 문자열)을 찾는 쿼리 유형입니다.
  • 색인이 생성된 데이터의 하위 집합에 대한 조건을 단일 색인 스캔으로 결합

검색 색인은 숫자 및 정확한 일치 문자열과 같은 텍스트 외 데이터의 색인 생성을 지원하지만 검색 색인의 사용 사례 대부분은 문서의 텍스트를 대상으로 합니다.

검색 색인 예시

검색 색인의 기능을 설명하기 위해 음악 앨범에 대한 정보를 저장하는 테이블이 있다고 가정해 보겠습니다.

CREATE TABLE Albums (
  AlbumId STRING(MAX) NOT NULL,
  AlbumTitle STRING(MAX)
) PRIMARY KEY(AlbumId);

Spanner에는 토큰을 만드는 여러 토큰화 함수가 있습니다. 사용자가 전체 텍스트 검색을 실행하여 앨범 제목을 찾을 수 있도록 이전 표를 수정하려면 TOKENIZE_FULLTEXT 함수를 사용하여 앨범 제목으로 토큰을 만듭니다. 그런 다음 TOKENLIST 데이터 유형을 사용하여 TOKENIZE_FULLTEXT에서의 토큰화 출력을 저장하는 열을 만듭니다. 이 예시에서는 AlbumTitle_Tokens 열을 만듭니다.

ALTER TABLE Albums
  ADD COLUMN AlbumTitle_Tokens TOKENLIST
  AS (TOKENIZE_FULLTEXT(AlbumTitle)) HIDDEN;

다음 예시에서는 CREATE SEARCH INDEX DDL을 사용하여 AlbumTitle 토큰(AlbumTitle_Tokens)에 대한 검색 색인(AlbumsIndex)을 만듭니다.

CREATE SEARCH INDEX AlbumsIndex
  ON Albums(AlbumTitle_Tokens);

검색 색인을 추가한 후 SQL 쿼리를 사용하여 검색 조건과 일치하는 앨범을 찾습니다. 예를 들면 다음과 같습니다.

SELECT AlbumId
FROM Albums
WHERE SEARCH(AlbumTitle_Tokens, "fifth symphony")

데이터 일관성

색인이 생성되면 Spanner는 자동 프로세스를 사용하여 데이터를 백필하여 일관성을 보장합니다. 쓰기가 커밋되면 색인이 동일한 트랜잭션에서 업데이트됩니다. Spanner에서 자동으로 데이터 일관성 검사를 수행합니다.

검색 색인 스키마 정의

검색 색인은 테이블의 하나 이상의 TOKENLIST 열에 정의됩니다. 검색 색인에는 다음 구성요소가 포함됩니다.

  • 기본 테이블: 색인 생성이 필요한 Spanner 테이블입니다.
  • TOKENLIST: 색인 생성이 필요한 토큰을 정의하는 열 컬렉션입니다. 이러한 열의 순서는 중요하지 않습니다.

예를 들어 다음 문에서 기본 테이블은 Albums입니다. TOKENLIST 열은 AlbumTitle(AlbumTitle_Tokens) 및 Rating(Rating_Tokens)에서 생성됩니다.

CREATE TABLE Albums (
  AlbumId STRING(MAX) NOT NULL,
  SingerId INT64 NOT NULL,
  ReleaseTimestamp INT64 NOT NULL,
  AlbumTitle STRING(MAX),
  Rating FLOAT64,
  AlbumTitle_Tokens TOKENLIST AS (TOKENIZE_FULLTEXT(AlbumTitle)) HIDDEN,
  Rating_Tokens TOKENLIST AS (TOKENIZE_NUMBER(Rating)) HIDDEN
) PRIMARY KEY(AlbumId);

다음 CREATE SEARCH INDEX 문을 사용하여 AlbumTitleRating에 대한 토큰으로 검색 색인을 만듭니다.

CREATE SEARCH INDEX AlbumsIndex
ON Albums(AlbumTitle_Tokens, Rating_Tokens)
PARTITION BY SingerId
ORDER BY ReleaseTimestamp DESC

검색 색인에는 다음 옵션이 있습니다.

  • 파티션: 검색 색인을 분할하는 선택적인 열 그룹입니다. 파티션을 나눈 색인을 쿼리하는 것이 파티션을 나누지 않은 색인을 쿼리하는 것보다 훨씬 효율적일 때가 많습니다. 자세한 내용은 파티션 검색 색인을 참조하세요.
  • 정렬 순서 열: 검색 색인에서 검색 순서를 설정하는 선택적인 INT64 열입니다. 자세한 내용은 검색 색인 정렬 순서를 참조하세요.
  • 인터리브 처리: 보조 색인과 마찬가지로 검색 색인도 인터리브 처리할 수 있습니다. 인터리브 처리된 검색 색인은 쓰기 및 기본 테이블 조인에 더 적은 리소스를 사용합니다. 자세한 내용은 인터리브 처리된 검색 색인을 참고하세요.
  • 옵션 절: 검색 색인의 기본 설정을 재정의하는 키-값 쌍 목록입니다.

자세한 내용은 CREATE SEARCH INDEX 참조 문서를 확인하세요.

검색 색인의 내부 레이아웃

검색 색인의 내부 표현에서 중요한 요소는 docid입니다. 이 요소는 임의의 길이가 될 수 있는 기본 테이블의 기본 키를 스토리지에 효율적인 표현 방식으로 나타냅니다. 또한 CREATE SEARCH INDEX 절의 사용자 제공 ORDER BY 열에 따라 내부 데이터 레이아웃의 순서를 결정합니다. 이 열은 하나 또는 2개의 64비트 정수로 저장됩니다.

검색 색인은 내부적으로 2가지 수준의 매핑으로 구현됩니다.

  1. docid에 대한 토큰
  2. 기본 테이블 기본 키에 대한 docid

이러한 스키마를 사용하면 Spanner가 각 <token, document> 쌍에 대해 전체 기본 테이블 기본 키를 저장할 필요가 없으므로 상당한 스토리지를 절약할 수 있습니다.

두 가지 매핑 수준을 구현하는 물리적 색인에는 두 가지 유형이 있습니다.

  1. 파티션 키와 docid를 기본 테이블 기본 키에 매핑하는 보조 색인. 이전 섹션의 예시에서 {SingerId, ReleaseTimestamp, uid}{AlbumId}에 매핑합니다. 보조 색인은 또한 CREATE SEARCH INDEXSTORING 절에 지정된 모든 열을 저장합니다.
  2. 토큰 색인은 정보 검색에서 일반적으로 설명되는 반전 색인과 유사하게 토큰을 docid에 매핑합니다. Spanner는 검색 색인의 각 TOKENLIST에 대해 별도의 토큰 색인을 유지합니다. 논리적으로 토큰 색인은 각 파티션 내에 각 토큰의 docid 목록을 저장합니다. 정보 검색에서는 이를 게시물 목록이라고 합니다. 이 목록은 빠른 검색을 위해 토큰별로 정렬되며 목록 내에서는 정렬을 위해 docid가 사용됩니다. 개별 토큰 색인은 Spanner API를 통해 노출되지 않는 구현 세부정보입니다.

Spanner는 docid에 대해 다음 4가지 옵션을 지원합니다.

검색 색인 docid 동작
검색 색인에서 ORDER BY 절이 생략됨 {uid} Spanner는 각 행을 식별하기 위해 숨겨진 고유한 값(UID)을 추가합니다.
ORDER BY column {column, uid} Spanner는 파티션 내에서 column 값이 동일한 행 간에 UID 열을 결정자로 추가합니다.
ORDER BY column ... OPTIONS (disable_automatic_uid_column=true) {column} UID 열이 추가되지 않았습니다. column 값은 파티션 내에서 고유해야 합니다.
ORDER BY column1, column2 ... OPTIONS (disable_automatic_uid_column=true) {column1, column2} UID 열이 추가되지 않았습니다. column1, column2 값의 조합은 파티션 내에서 고유해야 합니다.

사용 참고사항:

  • 내부 UID 열은 Spanner API를 통해 노출되지 않습니다.
  • UID가 추가되지 않은 색인의 경우 트랜잭션 중에 중복된 조합(파티션, 정렬 순서)으로 행을 추가하려는 시도가 실패합니다.

예를 들어 다음 데이터를 고려하세요.

AlbumId SingerId ReleaseTimestamp SongTitle
a1 1 997 Beautiful days
a2 1 743 Beautiful eyes

사전 정렬 열이 오름차순이라고 가정할 때 SingerId로 파티션을 나눈 토큰 색인의 콘텐츠는 다음과 같이 토큰 색인의 콘텐츠를 파티션으로 나눕니다.

SingerId _token ReleaseTimestamp uid
1 beautiful 743 uid1
1 beautiful 997 uid2
1 days 743 uid1
1 eyes 997 uid2

검색 색인 샤딩

Spanner는 테이블을 분할할 때 특정 기본 테이블 행에 있는 모든 토큰이 동일한 분할에 있도록 검색 색인 데이터를 분산합니다. 즉, 검색 색인이 문서로 샤딩됩니다. 이 샤딩 전략은 성능에 상당한 영향을 미칩니다.

  1. 각 트랜잭션이 통신하는 서버 수는 토큰 수 또는 색인이 생성된 TOKENLIST 열 수와 관계없이 일정하게 유지됩니다.
  2. 여러 조건부 표현식이 포함된 검색어는 각 분할에서 독립적으로 실행되므로 분산 조인과 관련된 성능 오버헤드를 방지할 수 있습니다.

검색 색인에는 두 가지 배포 모드가 있습니다.

  • 균일 샤딩(기본값). 균일 샤딩에서는 각 기본 테이블 행의 색인 생성된 데이터가 파티션의 색인 분할에 무작위로 할당됩니다.
  • 정렬 순서 샤딩. 정렬 순서 샤딩에서 각 기본 테이블 행의 데이터는 ORDER BY 열을 기준으로 파티션의 색인 분할에 할당됩니다. 예를 들어 내림차순 정렬 순서의 경우 정렬 순서 값이 가장 높은 모든 행이 파티션의 첫 번째 색인 분할에 표시되고 그 다음으로 높은 정렬 순서 값 그룹이 다음 분할에 표시됩니다.

이러한 샤딩 모드로 부하 집중 위험과 쿼리 비용 사이에 절충이 이루어집니다.

  • 정렬 순서 샤딩된 검색 색인은 색인이 타임스탬프를 기준으로 정렬될 때 부하 집중이 발생하기 쉽습니다. 자세한 내용은 기본 키 선택으로 핫스팟 방지를 참조하세요. 반면에 여러 문서에 대한 쓰기 부하가 증가하면 균일한 샤딩을 통해 증가량이 여러 샤드에 균일하게 분산됩니다.
  • 표준 부하 기반 분할은 부하 집중으로부터 적절한 보호를 제공하는 추가 분할을 만듭니다. 균일한 샤딩의 단점은 일부 유형의 쿼리에 더 많은 리소스를 사용할 수 있다는 것입니다.

검색 색인의 샤딩 모드는 OPTIONS 절을 사용하여 구성됩니다.

CREATE SEARCH INDEX AlbumsIndex
ON Albums(AlbumTitle_Tokens, Rating_Tokens)
PARTITION BY SingerId
ORDER BY ReleaseTimestamp DESC
OPTIONS (sort_order_sharding = true);

sort_order_sharding=false를 설정하거나 지정하지 않으면 균일한 샤딩을 사용하여 검색 색인이 생성됩니다.

인터리브 처리된 검색 색인

보조 색인과 마찬가지로 기본 테이블의 상위 테이블에서 검색 색인을 인터리브 처리할 수 있습니다. 인터리브 처리된 검색 색인을 사용하는 주된 이유는 작은 파티션의 색인 데이터와 기본 테이블 데이터를 같은 위치에 배치하기 위함입니다. 이러한 상황별 공동 배치에는 다음과 같은 이점이 있습니다.

  • 쓰기가 2단계 커밋을 실행할 필요가 없습니다.
  • 검색 색인과 기본 테이블의 백 조인이 배포되지 않습니다.

인터리브 처리된 검색 색인에는 다음과 같은 제한사항이 있습니다.

  1. 정렬 순서 샤딩된 색인만 인터리브 처리할 수 있습니다.
  2. 검색 색인은 하위 테이블이 아닌 최상위 테이블에서만 인터리브 처리될 수 있습니다.
  3. 인터리브 처리된 테이블 및 보조 색인과 마찬가지로 상위 테이블의 키를 인터리브 처리된 검색 색인의 PARTITION BY 열의 프리픽스로 만듭니다.

인터리브 처리된 검색 색인 정의

다음 예시에서는 인터리브 처리된 검색 색인을 정의하는 방법을 보여줍니다.

CREATE TABLE Singers (
  SingerId INT64 NOT NULL
) PRIMARY KEY(SingerId);

CREATE TABLE Albums (
  SingerId INT64 NOT NULL,
  AlbumId STRING(MAX) NOT NULL,
  AlbumTitle STRING(MAX),
  AlbumTitle_Tokens TOKENLIST AS (TOKENIZE_FULLTEXT(AlbumTitle)) HIDDEN
) PRIMARY KEY(SingerId, AlbumId),
  INTERLEAVE IN PARENT Singers ON DELETE CASCADE;

CREATE SEARCH INDEX AlbumsIndex
ON Albums(AlbumTitle_Tokens)
PARTITION BY SingerId,
INTERLEAVE IN Singers
OPTIONS (sort_order_sharding = true);

검색 색인 정렬 순서

검색 색인 정렬 순서 정의를 위한 요구사항은 보조 색인과 다릅니다.

예를 들어 다음 테이블을 살펴보세요.

CREATE TABLE Albums (
  AlbumId STRING(MAX) NOT NULL,
  ReleaseTimestamp INT64 NOT NULL,
  AlbumName STRING(MAX),
  AlbumName_Token TOKENLIST AS (TOKEN(AlbumName)) HIDDEN
) PRIMARY KEY(AlbumId);

애플리케이션은 ReleaseTimestamp순으로 정렬된 AlbumName을 사용하여 정보를 조회하는 보조 색인을 정의할 수 있습니다.

CREATE INDEX AlbumsSecondaryIndex ON Albums(AlbumName, ReleaseTimestamp DESC);

이에 상응하는 검색 색인은 다음과 같습니다(보조 색인에서 전체 텍스트 검색을 지원하지 않으므로 검색 색인은 일치검색 토큰화를 사용함).

CREATE SEARCH INDEX AlbumsSearchIndex
ON Albums(AlbumName_Token)
ORDER BY ReleaseTimestamp DESC;

검색 색인 정렬 순서는 다음 요구사항을 준수해야 합니다.

  1. 검색 색인 정렬 순서에는 INT64 열만 사용합니다. Spanner는 모든 토큰 옆에 docid를 저장해야 하므로 임의 크기의 열은 검색 색인에서 너무 많은 리소스를 사용합니다. 특히 TIMESTAMP는 64비트 정수에 맞지 않는 나노초 정밀도를 사용하므로 정렬 순서 열에서는 TIMESTAMP 유형을 사용할 수 없습니다.
  2. 정렬 순서 열은 NULL이면 안 됩니다. 이 요구사항을 충족하는 방법에는 두 가지가 있습니다.

    1. 정렬 순서 열을 NOT NULL로 선언합니다.
    2. NULL 값이 제외되도록 색인을 구성합니다.

타임스탬프는 정렬 순서를 결정하는 데 사용되는 경우가 많습니다. 일반적인 방법은 이러한 타임스탬프에 Unix epoch 이후 마이크로초를 사용하는 것입니다.

애플리케이션은 일반적으로 내림차순으로 정렬된 검색 색인을 사용하여 먼저 최신 데이터를 검색합니다.

NULL 필터링 검색 색인

검색 색인은 WHERE column IS NOT NULL 문법을 사용하여 기본 테이블 행을 제외할 수 있습니다. NULL 필터링은 파티셔닝 키, 정렬 순서 열, 저장된 열에 적용할 수 있습니다. 저장된 배열 열에 대한 NULL 필터링은 허용되지 않습니다.

예시

CREATE SEARCH INDEX AlbumsIndex
ON Albums(AlbumTitle_Tokens)
STORING Genre
WHERE Genre IS NOT NULL

쿼리에서 WHERE 절에 NULL 필터링 조건(이 예시의 경우 Genre IS NOT NULL)을 지정해야 합니다. 그렇지 않으면 쿼리 최적화 도구가 검색 색인을 사용할 수 없습니다. 자세한 내용은 SQL 쿼리 요구사항을 참조하세요.

생성된 열에서 NULL 필터링을 사용하여 임의의 기준에 따라 행을 제외합니다. 자세한 내용은 생성된 열을 사용하여 부분 색인 만들기를 참고하세요.

다음 단계