데이터 저장소 색인

App Engine은 항목의 각 속성에 대한 간단한 색인을 사전 정의합니다. App Engine 애플리케이션은 애플리케이션의 /war/WEB-INF/appengine-generated 디렉터리에 생성된 datastore-indexes.xml이라는 색인 구성 파일에서 커스텀 색인을 추가로 정의합니다. 개발 서버는 기존 색인으로 실행할 수 없는 쿼리를 발견하면 이 파일에 자동으로 제안 항목을 추가합니다. 애플리케이션을 업로드하기 전에 파일을 수정하여 색인을 수동으로 조정할 수 있습니다.

참고: 색인 기준 쿼리 메커니즘은 다양한 쿼리를 지원하고 대부분의 애플리케이션에 적합하지만 다른 데이터베이스 기술에서 일반적으로 사용되는 몇몇 종류의 쿼리는 지원하지 않습니다. 특히 조인 및 집계 쿼리는 Datastore 쿼리 엔진에서 지원되지 않습니다. Datastore 쿼리의 제한사항은 Datastore 쿼리 페이지를 참조하세요.

색인 정의 및 구조

색인은 지정된 항목 종류의 속성 목록에서 각 속성의 순서대로(오름차순 또는 내림차순) 정의됩니다. 상위 쿼리에 사용할 경우, 색인은 항목의 상위 항목을 선택적으로 포함할 수도 있습니다.

색인 표에는 색인 정의에서 명명된 모든 속성의 열이 포함됩니다. 테이블의 각 행은 색인을 기반으로 하는 쿼리의 잠재적 결과인 Datastore의 항목을 나타냅니다. 색인에 사용된 모든 속성의 색인 생성 값이 설정된 경우에만 항목이 색인에 포함됩니다. 색인 정의가 항목에 값이 없는 속성을 참조하는 경우, 해당 항목이 색인에 나타나지 않으므로 색인 기준의 쿼리 결과로 반환되지 않습니다.

참고: Datastore 모드 데이터베이스는 속성이 없는 항목과 null 값이 있는 속성이 있는 항목을 구분합니다(null). 항목의 속성에 null 값을 명시적으로 할당하면 항목은 해당 속성을 참조하는 쿼리의 결과에 포함될 수 있습니다.

참고: 여러 속성으로 구성된 색인을 사용하려면 각 속성에 대해 색인 없음으로 설정되어 있지 않아야 합니다.

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

  1. 일치 필터에 사용되는 속성
  2. 불일치 필터에 사용되는 속성(1개 이하여야 함)
  3. 정렬 순서에 사용되는 속성

이렇게 하면 실행 가능한 쿼리의 모든 결과가 표의 연속 행에 나타납니다. Datastore는 다음 단계에 따라 완벽한 색인을 사용하여 쿼리를 실행합니다.

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

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

Query q1 =
    new Query("Person")
        .setFilter(
            CompositeFilterOperator.and(
                new FilterPredicate("lastName", FilterOperator.EQUAL, "Smith"),
                new FilterPredicate("height", FilterOperator.EQUAL, 72)))
        .addSort("height", Query.SortDirection.DESCENDING);

이 쿼리의 완벽한 색인은 종류가 Person이고 lastNameheight 속성 값 열이 있는 항목의 키 테이블입니다. 색인은 먼저 lastName에 따라 오름차순으로 정렬된 다음 height에 따라 내림차순으로 정렬됩니다.

이러한 색인을 생성하려면 다음과 같이 색인을 구성합니다.

<?xml version="1.0" encoding="utf-8"?>
<datastore-indexes autoGenerate="false">
  <datastore-index kind="Person" ancestor="false" source="manual">
    <property name="lastName" direction="asc"/>
    <property name="height" direction="desc"/>
  </datastore-index>
</datastore-indexes>

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

Query q2 =
    new Query("Person")
        .setFilter(
            CompositeFilterOperator.and(
                new FilterPredicate("lastName", FilterOperator.EQUAL, "Jones"),
                new FilterPredicate("height", FilterOperator.EQUAL, 63)))
        .addSort("height", Query.SortDirection.DESCENDING);

다음 두 쿼리도 형식이 서로 다르지만 동일한 색인을 사용합니다.

Query q3 =
    new Query("Person")
        .setFilter(
            CompositeFilterOperator.and(
                new FilterPredicate("lastName", FilterOperator.EQUAL, "Friedkin"),
                new FilterPredicate("firstName", FilterOperator.EQUAL, "Damian")))
        .addSort("height", Query.SortDirection.ASCENDING);

Query q4 =
    new Query("Person")
        .setFilter(new FilterPredicate("lastName", FilterOperator.EQUAL, "Blair"))
        .addSort("firstName", Query.SortDirection.ASCENDING)
        .addSort("height", Query.SortDirection.ASCENDING);

색인 구성

기본적으로 Datastore는 각 항목 종류의 각 속성에 대한 색인을 자동으로 사전 정의합니다. 이렇게 사전 정의된 색인만으로도 일치 전용 쿼리 및 단순 불일치 쿼리 같이 여러 단순 쿼리를 수행할 수 있습니다. 다른 모든 쿼리의 경우 애플리케이션은 datastore-indexes.xml이라는 색인 구성 파일에 필요한 색인을 정의해야 합니다. 애플리케이션이 사용 가능한 색인(색인 구성 파일에 사전 정의되거나 지정됨)을 사용하여 실행할 수 없는 쿼리를 수행하려 하면 쿼리는 실패하고 DatastoreNeedIndexException이 발생합니다.

Datastore는 다음 형식의 쿼리에 대해 자동 색인을 빌드합니다.

  • 상위 및 키 필터만 사용하는 비구분 쿼리
  • 상위 및 일치 필터만 사용하는 쿼리
  • 불일치 필터만 사용하는 쿼리(단일 속성으로 제한됨)
  • 상위 필터, 속성의 일치 필터, 키의 불일치 필터만 사용하는 쿼리
  • 필터가 없고 속성 1개에 정렬 순서 1개(오름차순 또는 내림차순)만 있는 쿼리

다음을 포함한 다른 형식의 쿼리는 색인 구성 파일에서 색인을 지정해야 합니다.

  • 상위 및 불일치 필터가 있는 쿼리
  • 속성 1개에 불일치 필터가 1개 이상 있고 다른 속성에 일치 필터가 1개 이상 있는 쿼리
  • 키에 내림차순 정렬 순서가 있는 쿼리
  • 여러 정렬 순서가 있는 쿼리

색인 및 속성

아래에서는 색인에 대해 고려해야 할 몇 가지 특수 상황과 이러한 상황이 Datastore의 항목 속성과 어떤 연관이 있는지 알아봅니다.

값 유형이 혼합된 속성

두 항목에 이름이 같지만 값 유형이 서로 다른 속성이 있으면, 속성 색인이 항목을 먼저 값 유형별로 정렬한 다음 각 유형에 적절한 2차 순서대로 정렬합니다. 예를 들어 두 항목에 각각 이름이 age이며 하나는 정수 값, 하나는 문자열 값을 가진 속성이 있는 경우 age 속성에 따라 정렬할 때 속성 값 자체와는 관계없이 항상 정수 값을 가진 항목이 문자열 값을 가진 항목보다 앞에 옵니다.

이는 특히 Datastore에서 별도의 유형으로 처리되는 정수와 부동 소수점 숫자가 나올 때 유용합니다. 모든 정수는 모든 부동 소수점 이전에 정렬되므로 정수 값 38의 속성이 부동 소수점 값 37.5인 속성보다 먼저 정렬됩니다.

색인이 생성되지 않는 속성

특정 속성을 기준으로 필터링하거나 정렬할 필요가 없다고 판단되면 색인이 생성되지 않는 속성을 선언하여 Datastore에서 해당 속성의 색인 항목을 유지하지 않도록 할 수 있습니다. 이렇게 하면 수행해야 하는 Datastore 작성 횟수를 줄여 애플리케이션 실행 비용을 줄일 수 있습니다. 색인이 생성되지 않는 속성이 있는 항목은 속성이 설정되지 않은 것처럼 작동합니다. 즉, 색인이 생성되지 않는 속성의 필터 또는 정렬 순서를 사용하는 쿼리는 해당 항목과 일치하지 않습니다.

참고: 여러 속성으로 구성된 색인에 속성이 나타날 경우, 이를 색인이 생성되지 않는 속성으로 설정하면 구성된 색인에서 해당 속성의 색인이 생성되지 않습니다.

예를 들어 항목에 a 속성과 b 속성이 있고 WHERE a ="bike" and b="red"와 같은 쿼리를 충족할 수 있는 색인을 만든다고 가정해보겠습니다. 또한 WHERE a="bike"WHERE b="red" 쿼리에는 관심이 없다고 가정해 보겠습니다. a를 색인이 생성되지 않도록 설정하고 ab에 대한 색인을 만들 경우 Datastore는 ab의 색인 항목을 생성하지 않으므로 WHERE a="bike" and b="red" 쿼리가 작동하지 않습니다. Datastore에서 ab 색인의 항목을 만들려면 ab 모두에 색인을 생성해야 합니다.

하위 수준 Java Datastore API에서 속성은 항목을 설정하는 데 사용하는 메서드(setProperty() 또는 setUnindexedProperty())에 따라 항목별로 색인이 생성되거나 생성되지 않는 것으로 정의됩니다.

DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();

Key acmeKey = KeyFactory.createKey("Company", "Acme");

Entity tom = new Entity("Person", "Tom", acmeKey);
tom.setProperty("name", "Tom");
tom.setProperty("age", 32);
datastore.put(tom);

Entity lucy = new Entity("Person", "Lucy", acmeKey);
lucy.setProperty("name", "Lucy");
lucy.setUnindexedProperty("age", 29);
datastore.put(lucy);

Filter ageFilter = new FilterPredicate("age", FilterOperator.GREATER_THAN, 25);

Query q = new Query("Person").setAncestor(acmeKey).setFilter(ageFilter);

// Returns tom but not lucy, because her age is unindexed
List<Entity> results = datastore.prepare(q).asList(FetchOptions.Builder.withDefaults());

값을 setUnindexedProperty()로 재설정하여 색인이 생성되는 속성을 색인이 생성되지 않는 속성으로 변경하거나 값을 setProperty()로 재설정하여 색인이 생성되지 않는 속성을 색인이 생성되는 속성으로 변경할 수 있습니다.

색인이 생성되지 않는 속성을 색인이 생성되는 속성으로 변경해도 변경 전에 생성된 기존 항목은 영향을 받지 않습니다. 속성에서 쿼리를 필터링하면 이러한 기존 항목이 반환되지 않습니다. 생성 당시 쿼리의 색인에 이러한 항목을 작성하지 않았기 때문입니다. 향후 쿼리에서 이러한 항목에 액세스할 수 있게 하려면 항목을 Datastore에 다시 작성하여 적절한 색인에 입력되도록 해야 합니다. 즉, 이러한 기존 항목 각각에 대해 다음을 수행해야 합니다.

  1. Datastore에서 항목을 검색(가져오기)합니다.
  2. 항목을 Datastore에 다시 작성합니다(내보내기).

마찬가지로, 색인이 생성되는 속성을 색인이 생성되지 않는 속성으로 변경해도 이후에 Datastore에 작성된 항목만 영향을 받습니다. 색인이 생성되는 속성이 있는 기존 항목의 색인 항목은 업데이트되거나 삭제될 때까지 계속 그대로 남아있게 됩니다. 원하는 결과를 얻으려면 현재 색인이 생성되지 않는 속성을 기준으로 필터링 또는 정렬하는 모든 쿼리의 코드를 삭제해야 합니다.

색인 제한

Datastore는 단일 항목과 연결할 수 있는 색인 항목의 수 및 전체 크기에 제한을 적용합니다. 이러한 제한 사항은 크며 대부분의 애플리케이션은 영향을 받지 않습니다. 하지만 이러한 제한이 발생할 수 있는 상황이 있습니다.

설명대로 Datastore는 긴 텍스트 문자열(Text), 긴 바이트 문자열(Blob), 삽입된 항목(EmbeddedEntity), 명시적으로 색인이 생성되지 않음으로 선언된 항목을 제외한 모든 항목의 사전 정의된 색인에 항목을 만듭니다. 이 속성은 datastore-indexes.xml 구성 파일에서 선언된 추가 커스텀 색인에 포함될 수도 있습니다. 항목에 목록 속성이 없다면 해당 커스텀 색인(비상위 색인용) 각각에 대해 최대 1개의 항목 또는 항목의 상위 항목(상위 색인용) 각각에 대해 1개의 항목이 있을 것입니다. 속성 값이 변경될 때마다 이러한 색인 항목 각각을 업데이트해야 합니다.

항목마다 단일 값을 포함하는 속성의 경우, 가능한 각각의 값은 속성의 사전 정의된 색인에 항목당 한 번씩만 저장해야 합니다. 이렇게 해도 이러한 단일 값 속성이 많은 항목이 색인 항목 또는 크기 제한을 초과할 수 있습니다. 마찬가지로, 동일한 속성의 여러 값이 있을 수 있는 항목에는 각 값에 대한 별도의 색인 항목이 필요합니다. 여기서도 가능한 값의 수가 많으면 이러한 항목이 항목 제한을 초과할 수 있습니다.

속성이 여러 개인 항목의 경우, 상황이 더 복잡해질 수 있습니다. 이러한 속성 각각에 여러 값이 사용될 수 있기 때문입니다. 이러한 항목을 수용하려면 속성 값의 모든 가능한 조합을 위한 항목이 색인에 포함되어야 합니다. 각각의 값이 여러 개인 여러 속성을 참조하는 커스텀 색인은 조합을 통해 '과도'해질 수 있습니다. 이 경우, 상대적으로 적은 수의 가능한 속성 값만 있는 항목에 다수의 항목이 필요합니다. 이러한 과도 색인의 경우 업데이트해야 하는 색인 항목이 많으므로 Datastore에 항목을 작성하는 비용이 크게 증가할 수 있으며 항목이 색인 항목 또는 크기 제한을 쉽게 초과할 수도 있습니다.

쿼리 살펴보기

Query q =
    new Query("Widget")
        .setFilter(
            CompositeFilterOperator.and(
                new FilterPredicate("x", FilterOperator.EQUAL, 1),
                new FilterPredicate("y", FilterOperator.EQUAL, 2)))
        .addSort("date", Query.SortDirection.ASCENDING);

이 쿼리를 실행하면 SDK가 다음 색인을 제안합니다.

<?xml version="1.0" encoding="utf-8"?>
<datastore-indexes autoGenerate="false">
  <datastore-index kind="Widget" ancestor="false" source="manual">
    <property name="x" direction="asc"/>
    <property name="y" direction="asc"/>
    <property name="date" direction="asc"/>
  </datastore-index>
</datastore-indexes>
이 색인에서는 항목마다 총 |x| * |y| * |date|개 항목이 필요합니다. 여기서 |x|x 속성의 항목과 연결된 값의 수를 나타냅니다. 다음 코드를 예시로 들어 보겠습니다.
Entity widget = new Entity("Widget");
widget.setProperty("x", Arrays.asList(1, 2, 3, 4));
widget.setProperty("y", Arrays.asList("red", "green", "blue"));
widget.setProperty("date", new Date());
datastore.put(widget);

이 코드는 x 속성 값이 4개이고 y 속성 값이 3개이며 date가 현재 날짜로 설정된 항목을 만듭니다. 이 경우 가능한 속성 값 조합마다 1개씩, 총 12개의 색인 항목이 필요합니다.

(1, "red", <now>) (1, "green", <now>) (1, "blue", <now>)

(2, "red", <now>) (2, "green", <now>) (2, "blue", <now>)

(3, "red", <now>) (3, "green", <now>) (3, "blue", <now>)

(4, "red", <now>) (4, "green", <now>) (4, "blue", <now>)

동일한 속성이 여러 번 반복되면 Datastore는 과도 색인을 감지하고 대체 색인을 제안할 수 있습니다. 하지만 다른 모든 상황(예: 이 예시에 정의된 쿼리)에서는 Datastore가 과도 색인을 생성합니다. 이 경우 색인 구성 파일에서 수동으로 색인을 구성하여 과도 색인을 피할 수 있습니다.

<?xml version="1.0" encoding="utf-8"?>
<datastore-indexes autoGenerate="false">
  <datastore-index kind="Widget">
    <property name="x" direction="asc" />
    <property name="date" direction="asc" />
  </datastore-index>
  <datastore-index kind="Widget">
    <property name="y" direction="asc" />
    <property name="date" direction="asc" />
  </datastore-index>
</datastore-indexes>
이렇게 하면 필요한 항목 수가 12개가 아닌 7개((|x| * |date| + |y| * |date|))로 줄어듭니다.

(1, <now>) (2, <now>) (3, <now>) (4, <now>)

("red", <now>) ("green", <now>) ("blue", <now>)

색인이 색인 항목 또는 크기 제한을 초과할 수 있게 하는 모든 내보내기 작업은 실패하고 IllegalArgumentException이 발생합니다. 예외 텍스트를 통해 초과한 제한("Too many indexed properties" 또는 "Index entries too large")과 원인으로 작용한 커스텀 색인을 확인할 수 있습니다. 빌드 시 항목 제한을 초과하는 새 색인을 만들면 해당 색인에 대한 쿼리가 실패하고 색인은 Google Cloud 콘솔에서 Error 상태로 표시됩니다. Error 상태의 색인을 해결하려면 다음 안내를 따르세요.

  1. datastore-indexes.xml 파일에서 Error 상태의 색인을 삭제합니다.

  2. datastore-indexes.xml이 있는 디렉터리에서 다음 명령어를 실행하여 Datastore에서 색인을 삭제합니다.

    gcloud datastore indexes cleanup datastore-indexes.xml
    
  3. 오류의 원인을 해결합니다. 예를 들면 다음과 같습니다.

    • 색인 정의와 해당 쿼리를 다시 작성합니다.
    • 과도 색인을 발생시키는 항목을 삭제합니다.
  4. 색인을 다시 datastore-indexes.xml 파일에 추가합니다.

  5. datastore-indexes.xml이 있는 디렉터리에서 다음 명령어를 실행하여 Datastore에 색인을 만듭니다.

    gcloud datastore indexes create datastore-indexes.xml
    

목록 속성을 사용하는 커스텀 색인이 필요한 쿼리를 사용하지 않으면 과도 색인을 방지할 수 있습니다. 위에서 설명한 대로 여기에는 여러 정렬 순서를 사용하는 쿼리나 일치 필터와 불일치 필터가 혼합된 쿼리가 포함됩니다.