색인 최적화

이 페이지에서는 앱에 Datastore 모드 Firestore 색인을 선택할 때 고려해야 할 개념을 설명합니다.

Datastore 모드 Firestore는 모든 쿼리에 색인을 사용하여 높은 쿼리 성능을 제공합니다. 대부분의 쿼리 성능은 데이터베이스의 전체 크기가 아닌 결과 집합의 크기에 따라 달라집니다.

Datastore 모드의 Firestore는 항목의 각 속성에 대해 기본 제공되는 색인을 정의합니다. 이러한 단일 속성 색인은 여러 간단한 쿼리를 지원합니다. Datastore 모드의 Firestore는 데이터베이스가 기본 제공 색인을 병합하여 추가 쿼리를 지원할 수 있는 색인 병합 기능을 지원합니다. 보다 복잡한 쿼리의 경우 복합 색인을 미리 정의해야 합니다.

이 페이지에서는 색인 병합 기능을 주로 다룹니다. 두 가지 중요한 색인 최적화 기능에 영향을 주기 때문입니다.

  • 쿼리 속도 향상
  • 복합 색인의 수 감소

다음 예시는 색인 병합 기능을 보여줍니다.

Photo 항목 필터링

Photo 종류의 항목이 있는 Datastore 모드 데이터베이스를 생각해 보세요.

사진
속성 값 유형 설명
owner_id 문자열 사용자 ID
tag 문자열 배열 토큰화된 키워드
size 정수 열거:
  • 1 icon
  • 2 medium
  • 3 large
coloration 정수 열거:
  • 1 black & white
  • 2 color

다음의 논리 AND에 기준하여 사용자가 Photo 항목을 쿼리할 수 있는 앱이 필요하다고 가정해 보세요.

  • 다음 속성을 기준으로 최대 3개의 필터:

    • owner_id
    • size
    • coloration
  • tag 검색 문자열. 앱은 검색 문자열을 태그로 토큰화하고 태그마다 필터를 추가합니다.

    예를 들어 앱은 검색 문자열 outside, family를 쿼리 필터 tag=outsidetag=family로 변환합니다.

기본 제공 색인과 Datastore 모드 Firestore의 색인 병합 기능을 사용하면 복합 색인을 추가하지 않고도 이 Photo 필터 기능의 색인 요구사항을 충족할 수 있습니다.

Photo 항목의 기본 제공 색인은 다음과 같은 단일 필터 쿼리를 지원합니다.

Python

from google.cloud import datastore

# For help authenticating your client, visit
# https://cloud.google.com/docs/authentication/getting-started
client = datastore.Client()

query_owner_id = client.query(kind="Photo", filters=[("owner_id", "=", "user1234")])

query_size = client.query(kind="Photo", filters=[("size", "=", 2)])

query_coloration = client.query(kind="Photo", filters=[("coloration", "=", 2)])

Photo 필터 기능에는 논리 AND로 여러 일치 필터를 결합하는 쿼리도 필요합니다.

Python

from google.cloud import datastore

# For help authenticating your client, visit
# https://cloud.google.com/docs/authentication/getting-started
client = datastore.Client()

query_all_properties = client.query(
    kind="Photo",
    filters=[
        ("owner_id", "=", "user1234"),
        ("size", "=", 2),
        ("coloration", "=", 2),
        ("tag", "=", "family"),
    ],
)

Datastore 모드의 Firestore는 기본 제공 색인을 병합하여 이러한 쿼리를 지원할 수 있습니다.

색인 병합

Datastore 모드의 Firestore는 쿼리와 색인이 다음의 모든 제약조건을 충족하는 경우 색인 병합을 사용할 수 있습니다.

  • 쿼리는 일치(=) 필터만 사용합니다.
  • 쿼리와 필터의 순서가 완벽하게 일치하는 복합 색인이 없습니다.
  • 각 일치 필터는 적어도 하나의 기존 색인을 쿼리와 동일한 순서에 일치시킵니다.

이 경우 Datastore 모드의 Firestore는 추가 복합 색인을 구성하지 않고도 기존 색인을 사용하여 쿼리를 지원할 수 있습니다.

두 개 이상의 색인이 같은 기준으로 정렬되면 Datastore 모드의 Firestore는 여러 색인 스캔 결과를 병합하여 이러한 모든 색인에 공통된 결과를 찾을 수 있습니다. Datastore 모드의 Firestore는 모든 기본 제공 색인이 항목 키별로 값을 정렬하므로 기본 제공 색인을 병합할 수 있습니다.

Datastore 모드의 Firestore는 기본 제공 색인을 병합하여 여러 속성에서 일치 필터가 있는 쿼리를 지원합니다.

Python

from google.cloud import datastore

# For help authenticating your client, visit
# https://cloud.google.com/docs/authentication/getting-started
client = datastore.Client()

query_all_properties = client.query(
    kind="Photo",
    filters=[
        ("owner_id", "=", "user1234"),
        ("size", "=", 2),
        ("coloration", "=", 2),
        ("tag", "=", "family"),
    ],
)

Datastore 모드의 Firestore는 동일한 색인의 여러 섹션에서 색인 결과를 병합할 수도 있습니다. Datastore 모드의 Firestore는 tag 속성의 기본 제공 색인의 다양한 섹션을 병합하여 여러 tag 필터를 논리 AND로 결합하는 쿼리를 지원합니다.

Python

from google.cloud import datastore

# For help authenticating your client, visit
# https://cloud.google.com/docs/authentication/getting-started
client = datastore.Client()

query_tag = client.query(
    kind="Photo",
    filters=[
        ("tag", "=", "family"),
        ("tag", "=", "outside"),
        ("tag", "=", "camping"),
    ],
)

query_owner_size_color_tags = client.query(
    kind="Photo",
    filters=[
        ("owner_id", "=", "user1234"),
        ("size", "=", 2),
        ("coloration", "=", 2),
        ("tag", "=", "family"),
        ("tag", "=", "outside"),
        ("tag", "=", "camping"),
    ],
)

병합된 기본 제공 색인이 지원하는 쿼리는 Photo 필터링 기능에 필요한 쿼리 집합을 완성합니다. Photo 필터링 기능 지원에는 추가 복합 색인이 필요하지 않았습니다.

앱에 가장 적합한 색인을 선택할 때는 색인 병합 기능을 이해하는 것이 중요합니다. 색인 병합으로 Datastore 모드 Firestore의 쿼리 유연성은 높아지지만 성능이 하락할 수 있습니다. 다음 섹션에서는 색인 병합의 성능과 복합 색인을 추가하여 성능을 개선하는 방법을 설명합니다.

완벽한 색인 찾기

색인은 색인 정의에 지정된 순서대로 먼저 상위 항목별로 정렬된 후 속성 값별로 정렬됩니다. 쿼리를 가장 효율적으로 실행할 쿼리의 완벽한 복합 색인은 다음 속성에 다음 순서대로 정의됩니다.

  1. 균등 필터에 사용되는 속성
  2. 정렬 순서에 사용되는 속성
  3. distinctOn 필터에 사용되는 속성
  4. (아직 정렬 순서에 포함되지 않은) 범위 및 불일치 필터에 사용되는 속성
  5. (아직 정렬 순서와 범위 및 불일치 필터에 포함되지 않은) 집계 및 프로젝션에 사용되는 속성

이렇게 하면 실행 가능한 쿼리의 모든 결과가 고려됩니다. Datastore 모드 데이터베이스의 Firestore는 다음 단계를 통해 완벽한 색인을 사용하여 쿼리를 실행합니다.

  1. 쿼리의 종류, 필터 속성, 필터 연산자, 정렬 순서에 해당하는 색인을 식별합니다.
  2. 색인 시작 부분부터 쿼리의 필터 조건을 모두 또는 일부 충족하는 첫 번째 항목까지 스캔합니다.
  3. 색인을 계속 스캔하여 다음 경우가 될 때까지 모든 필터 조건을 충족하는 각 항목을 반환합니다.
    • 필터 조건을 충족하지 않는 항목을 발견한 경우 또는
    • 색인 끝에 도달한 경우 또는
    • 쿼리에서 요청한 최대 개수의 결과를 수집한 경우

예를 들어, 다음 쿼리를 살펴보겠습니다.

SELECT * FROM Task
WHERE category = 'Personal'
  AND priority < 3
ORDER BY priority DESC

이 쿼리의 맞춤형 복합 색인은 종류가 Task이고 categorypriority 속성 값 열이 있는 항목의 키 색인입니다. 색인은 먼저 category에 따라 오름차순으로 정렬된 다음 priority에 따라 내림차순으로 정렬됩니다.

indexes:
- kind: Task
  properties:
  - name: category
    direction: asc
  - name: priority
    direction: desc

형식이 동일하지만 필터 값이 서로 다른 두 쿼리가 동일한 색인을 사용합니다. 예를 들어 다음 쿼리에서는 이전 쿼리와 동일한 색인을 사용합니다.

SELECT * FROM Task
WHERE category = 'Work'
  AND priority < 5
ORDER BY priority DESC

이 색인의 경우

indexes:
- kind: Task
  properties:
  - name: category
    direction: asc
  - name: priority
    direction: asc
  - name: created
    direction: asc

앞선 색인은 다음 쿼리를 모두 충족할 수 있습니다.

SELECT * FROM Task
WHERE category = 'Personal'
  AND priority = 5
ORDER BY created ASC

SELECT * FROM Task
WHERE category = 'Work'
ORDER BY priority ASC, created ASC

색인 선택 최적화

이 섹션에서는 색인 병합의 성능 특성과 색인 병합과 관련된 다음 두 가지 최적화 기회를 설명합니다.

  • 복합 색인을 추가하여 병합된 색인에 의존하는 쿼리 속도 높이기
  • 병합된 색인을 활용하여 복합 색인 수 줄이기

색인 병합 성능

색인 병합에서 Datastore 모드의 Firestore는 지그재그 병합 조인 알고리즘을 사용하여 색인을 효율적으로 병합합니다. 이 알고리즘을 사용하면 Datastore 모드는 여러 색인 스캔의 잠재적 일치 항목을 조인하여 쿼리와 일치하는 결과 집합을 생성합니다. 색인 병합은 쓰기 시간 대신 읽기 시간에 필터 구성요소를 결합합니다. 성능이 결과 집합의 크기에만 의존하는 대부분의 Datastore 모드 Firestore 쿼리와 달리 색인 병합 쿼리의 성능은 쿼리의 필터와 데이터베이스에서 고려하는 잠재적 일치 항목 수에 따라 달라집니다.

색인 병합의 최상의 성능은 색인의 모든 잠재적 일치 항목이 쿼리 필터를 충족할 때 발생합니다. 이 경우 성능은 O(R * I)입니다. 여기서 R은 결과 집합의 크기이고 I는 스캔된 색인 수입니다.

최악의 성능은 데이터베이스가 여러 잠재적 일치 항목을 고려해야 하지만 쿼리 필터를 만족하는 일치 항목이 거의 없는 경우에 발생합니다. 이 경우 성능은 O(S)입니다. 여기서 S는 단일 색인 스캔에서 가장 작은 잠재적 항목 집합의 크기입니다.

실제 성능은 데이터의 모양에 따라 달라집니다. 반환된 각 결과에 대해 고려되는 항목의 평균 개수는 O(S/(R * I))입니다. 각 색인 스캔과 일치하는 항목은 많지만 전체 쿼리와 일치하는 항목이 거의 없는 경우, 즉 R이 작고 S가 큰 경우에는 쿼리 성능이 나빠집니다.

이러한 위험을 최소화하는 네 가지 방법은 다음과 같습니다.

  • 쿼리 플래너는 항목이 전체 쿼리와 일치하는 것을 확인할 때까지 항목을 조회하지 않습니다.

  • 지그재그 알고리즘은 다음 결과를 반환하기 위해 모든 결과를 찾을 필요가 없습니다. 첫 10개의 결과를 요청하면 이 10개의 결과를 찾기 위한 지연 시간에 대해서만 비용을 지불합니다.

  • 지그재그 알고리즘은 대규모의 거짓 양성 결과 섹션을 건너뜁니다. 최악의 성능이 발생하는 단 하나의 경우는 스캔 사이에 거짓 양성 결과가 완벽하게 뒤섞여있을(정렬 순서에서) 때 입니다.

  • 지연 시간은 각 필터와 일치하는 항목 수가 아니라 각 색인 스캔에서 발견된 항목 수에 따라 달라집니다. 다음 섹션에 나온 것처럼 복합 색인을 추가하여 색인 병합 성능을 향상시킬 수 있습니다.

색인 병합 쿼리 속도 향상

Datastore 모드의 Firestore가 색인을 병합하면 각 색인 스캔은 종종 쿼리의 단일 필터에 매핑됩니다. 쿼리에서 여러 필터와 일치하는 복합 색인을 추가하면 쿼리 성능을 향상시킬 수 있습니다.

다음 쿼리를 생각해 보세요.

Python

from google.cloud import datastore

# For help authenticating your client, visit
# https://cloud.google.com/docs/authentication/getting-started
client = datastore.Client()

query_owner_size_tag = client.query(
    kind="Photo",
    filters=[
        ("owner_id", "=", "username"),
        ("size", "=", 2),
        ("tag", "=", "family"),
    ],
)

각 필터는 다음과 같은 기본 제공 색인의 색인 스캔 하나에 매핑됩니다.

Index(Photo, owner_id)
Index(Photo, size)
Index(Photo, tag)

복합 색인 Index(Photo, owner_id, size)를 추가하면 쿼리는 3개가 아닌 2개의 색인 스캔에 매핑됩니다.

#  Satisfies both 'owner_id=username' and 'size=2'
Index(Photo, owner_id, size)
Index(Photo, tag)

큰 이미지와 흑백 이미지가 많지만 큰 파노라마 이미지는 거의 없는 시나리오를 생각해 보세요. 기본 제공 색인을 병합하면 파노라마 이미지 및 흑백 이미지의 쿼리 필터링 속도가 느려집니다.

Python

from google.cloud import datastore

# For help authenticating your client, visit
# https://cloud.google.com/docs/authentication/getting-started
client = datastore.Client()

query_size_coloration = client.query(
    kind="Photo", filters=[("size", "=", 2), ("coloration", "=", 1)]
)

쿼리 성능을 개선하기 위해 다음 복합 색인을 추가하여 O(S/(R * I))에서 S(단일 색인 스캔의 가장 작은 항목 집합) 값을 낮출 수 있습니다.

Index(Photo, size, coloration)

2개의 기본 제공 색인을 사용하는 경우와 비교할 때 이 복합 색인에서는 동일한 쿼리 필터 2개의 잠재적 결과가 더 적게 생성됩니다. 이 방법은 색인을 하나 더 추가하는 대신 성능을 대폭 향상시킵니다.

색인 병합을 사용하여 복합 색인의 수 줄이기

쿼리의 필터와 정확히 일치하는 복합 색인의 성능이 가장 좋지만 모든 필터 조합에 복합 색인을 추가하는 것이 항상 최선이거나 가능한 것은 아닙니다. 다음을 기준으로 적절히 복합 색인을 추가해야 합니다.

  • 복합 색인 한도:

    한도
    데이터베이스의 최대 복합 색인 수
    항목의 복합 색인 항목 크기 최대 총합 2MiB
    다음 항목의 최대 총합:
    • 색인 생성된 속성 값 수
    • 복합 색인 항목 수
    20,000
  • 각 추가 색인의 스토리지 비용
  • 쓰기 지연 시간에 미치는 영향

색인 생성 문제는 Photo 항목의 tag 속성과 같이 여러 값을 갖는 필드에서 종종 발생합니다.

예를 들어 Photo 필터링 기능이 다음 네 가지 추가 속성을 기반으로 내림차순 순서 지정 절을 지원해야 한다고 가정해 보겠습니다.

사진
속성 값 유형 설명
date_added 정수 날짜/시간
rating 부동 소수점 수 총 사용자 평가
comment_count 정수 댓글 수
download_count 정수 다운로드 횟수

tag 필드를 무시하는 경우, Photo 필터의 모든 조합과 일치하는 복합 색인을 선택할 수 있습니다.

Index(Photo, owner_id, -date_added)
Index(Photo, owner_id, -comments)
Index(Photo, size, -date_added)
Index(Photo, size, -comments)
...
Index(Photo, owner_id, size, -date_added)
Index(Photo, owner_id, size, -comments)
...
Index(Photo, owner_id, size, coloration, -date_added)
Index(Photo, owner_id, size, coloration, -comments)

총 복합 색인 수는 다음과 같습니다.

2^(number of filters) * (number of different orders) = 2 ^ 3 * 4 = 32 composite indexes

최대 3개의 tag 필터를 지원하려고 하면 총 복합 색인 수는 다음과 같습니다.

2 ^ (3 + 3 tag filters) * 4 = 256 indexes.

tag와 같이 값이 여러 개인 속성을 포함하는 색인도 과도 색인 문제를 일으켜 스토리지 비용과 쓰기 지연 시간이 늘어날 수 있습니다.

이 기능의 tag 필드에서 필터를 지원하려면 병합된 색인을 사용하여 총 색인 수를 줄일 수 있습니다. 다음 복합 색인 집합은 Photo 필터링 기능을 지원하는 데 필요한 최소한의 집합으로 다음과 같이 순서를 지정합니다.

Index(Photo, owner_id, -date_added)
Index(Photo, owner_id, -rating)
Index(Photo, owner_id, -comments)
Index(Photo, owner_id, -downloads)
Index(Photo, size, -date_added)
Index(Photo, size, -rating)
Index(Photo, size, -comments)
Index(Photo, size, -downloads)
...
Index(Photo, tag, -date_added)
Index(Photo, tag, -rating)
Index(Photo, tag, -comments)
Index(Photo, tag, -downloads)

정의된 복합 색인의 수는 다음과 같습니다.

(number of filters + 1) * (number of orders) = 7 * 4 = 28

색인 병합에는 다음과 같은 이점도 있습니다.

  • 쿼리당 tag 필터 수 제한 없이 Photo 항목이 최대 1,000개의 태그를 지원할 수 있습니다.
  • 총 색인 수를 줄여 스토리지 비용과 쓰기 지연 시간을 줄입니다.

앱 색인 선택

다음 두 가지 방법을 사용하여 Datastore 모드 데이터베이스에 최적인 색인을 선택할 수 있습니다.

  • 색인 병합을 사용하여 추가 쿼리 지원

    • 필요한 복합 색인 수 감소
    • 항목당 스토리지 비용 절감
    • 쓰기 지연 시간 단축
    • 과도 색인 방지
    • 성능이 데이터의 모양에 따라 달라짐
  • 쿼리에서 여러 필터와 일치하는 복합 색인 정의

    • 쿼리 성능 향상
    • 데이터 모양에 영향을 받지 않는 일관된 쿼리 성능
    • 복합 색인 한도 미만으로 유지되어야 함
    • 항목당 스토리지 비용 증가
    • 쓰기 지연 시간 증가

앱에 최적인 색인은 데이터 모양의 변화에 따라 바뀔 수 있습니다. 쿼리 성능 샘플링을 통해 앱의 일반적 쿼리와 느린 쿼리를 파악할 수 있습니다. 이 정보를 사용하여 색인을 추가하면 일반적 쿼리와 느린 쿼리의 성능을 향상시킬 수 있습니다.