기존 번들 서비스용 App Engine Datastore API

참고: 새로운 애플리케이션을 빌드하는 개발자는 NDB 클라이언트 라이브러리를 사용하는 것이 좋습니다. NDB 클라이언트 라이브러리는 이 클라이언트 라이브러리와 비교할 때 Memcache API를 통한 자동 항목 캐싱과 같은 여러 이점이 있습니다. 현재 이전 DB 클라이언트 라이브러리를 사용 중인 경우 DB에서 NDB로의 마이그레이션 가이드를 참조하세요.

이 문서에서는 Datastore에 저장된 객체의 데이터 모델, API를 사용하여 쿼리를 구성하는 방법, 트랜잭션을 처리하는 방법을 설명합니다.

항목

Datastore의 객체를 항목이라고 합니다. 항목에는 명명된 속성이 한 개 이상 있으며 각 속성에는 값이 한 개 이상 있을 수 있습니다. 속성 값은 정수, 부동 소수점 수, 문자열, 날짜, 바이너리 데이터 등 다양한 데이터 유형에 속할 수 있습니다. 값이 여러 개인 속성에 대한 쿼리는 값이 쿼리 기준을 충족하는지 여부를 테스트합니다. 따라서 이러한 속성은 멤버십 테스트에 유용합니다.

종류, 키, 식별자

각 Datastore 항목은 쿼리 목적으로 항목을 분류하는 특정 종류에 속합니다. 예를 들어 인사 관리 애플리케이션은 회사의 각 직원을 종류가 Employee인 항목으로 나타낼 수 있습니다. 또한 각 항목에는 해당 항목을 고유하게 식별하는 자체 가 있습니다. 키는 다음 구성요소로 구성됩니다.

  • 항목의 종류
  • 식별자(다음 중 하나일 수 있음)
    • 키 이름 문자열
    • 정수 ID
  • Datastore 계층 구조에서 항목 위치를 나타내는 선택적 상위 경로

식별자는 항목이 생성될 때 할당됩니다. 식별자는 항목 키의 일부이므로 항목과 영구적으로 연관되며 변경할 수 없습니다. 식별자는 다음의 두 가지 방법으로 할당할 수 있습니다.

  • 애플리케이션에서 항목의 키 이름 문자열을 직접 지정합니다.
  • Datastore에서 항목에 정수 숫자 ID를 자동으로 할당하도록 할 수 있습니다.

상위 경로

Cloud Datastore의 항목은 파일 시스템의 디렉터리 구조와 유사한 계층 구조 형식의 공간을 형성합니다. 항목을 만들 때 선택적으로 다른 항목을 상위 요소로 지정할 수 있으며, 새 항목은 상위 항목의 하위 요소가 됩니다. 파일 시스템과 달리 상위 항목이 실제로 존재할 필요는 없습니다. 상위 요소가 없는 항목은 루트 항목입니다. 항목과 상위 요소 간의 연결은 영구적이며 항목이 생성되면 변경할 수 없습니다. Cloud Datastore는 상위 요소가 동일한 2개의 항목 또는 2개의 루트 항목(상위 요소가 없는 항목)에 동일한 숫자 ID를 할당하지 않습니다.

항목의 상위 요소, 상위 요소의 상위 요소 등은 재귀적으로 해당 항목의 상위가 되고, 항목의 하위 요소, 하위 요소의 하위 요소 등은 해당 항목의 하위가 됩니다. 루트 항목과 그 하위에 있는 모든 항목은 동일한 항목 그룹에 속합니다. 루트 항목에서 시작하여 상위 요소에서 하위 요소로 진행되고 지정된 항목까지 이어지는 항목의 시퀀스를 해당 항목의 상위 경로라고 합니다. 항목을 식별하는 전체 키는 상위 경로를 지정하고 항목 자체로 종료되는 종류-식별자 쌍의 시퀀스로 구성됩니다.

[Person:GreatGrandpa, Person:Grandpa, Person:Dad, Person:Me]

루트 항목의 경우 상위 경로는 비어 있으며 키는 항목의 고유한 종류 및 식별자로만 구성됩니다.

[Person:GreatGrandpa]

이 개념은 다음 다이어그램에서 설명합니다.

항목 그룹에서 루트 항목과 하위 항목의 관계 표시

쿼리 및 색인

애플리케이션은 키를 사용하여 직접 Datastore의 항목을 검색할 뿐만 아니라 속성 값을 사용하여 항목을 검색하는 쿼리를 수행할 수 있습니다. 쿼리는 지정된 종류의 항목을 대상으로 작동하며, 항목의 속성 값, 키, 상위 항목에 필터를 지정하고 0개 이상의 항목을 결과로 반환할 수 있습니다. 또한 정렬 순서를 지정하여 결과를 속성 값에 따라 순차적으로 배열할 수 있습니다. 결과에는 필터와 정렬 순서에 명시된 모든 속성에 대한 값이 하나(null 가능) 이상 있으며 해당 속성 값이 지정된 모든 필터 기준을 충족하는 모든 항목이 포함됩니다. 쿼리는 전체 항목 또는 예상 항목을 반환하거나 항목 만 반환할 수도 있습니다.

일반적인 쿼리에는 다음이 포함됩니다.

  • 쿼리가 적용되는 항목 종류
  • 항목의 속성 값, 키, 상위 요소를 기반으로 하는 0개 이상의 필터
  • 결과를 순차적으로 배열하는 0개 이상의 정렬 순서
쿼리가 실행되면 해당 종류의 모든 항목 중 지정된 필터를 모두 충족하는 항목이 지정된 정렬 순서에 따라 검색됩니다. 쿼리는 읽기 전용으로 실행됩니다.

참고: 메모리를 절약하고 성능을 개선하려면 가능한 경우 쿼리가 반환하는 결과 수를 제한해야 합니다.

지정된 상위 항목으로부터 이어지는 항목 그룹으로만 결과를 제한하는 상위 필터를 쿼리에 포함할 수도 있습니다. 이러한 쿼리를 상위 쿼리라고 합니다. 기본적으로 상위 쿼리는 데이터의 최신 변경사항을 항상 반영하는 strong consistency를 가진 결과를 반환합니다. 반면 상위 쿼리가 아닌 쿼리는 단일 항목 그룹보다는 전체 Datastore를 대상으로 하지만 eventual consistency만 갖도록 보장하므로 오래된 결과를 반환할 수도 있습니다. 애플리케이션에서 strong consistency가 중요한 경우에는 데이터를 구성할 때 이를 고려하여 관련 항목은 같은 항목 그룹에 배치해야 합니다. 그래야 다른 쿼리가 아닌 상위 쿼리로 검색할 수 있기 때문입니다. 자세한 내용은 strong consistency를 위한 데이터 구조화를 참조하세요.

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

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

트랜잭션

항목을 삽입, 업데이트, 삭제하려는 모든 시도는 트랜잭션의 컨텍스트에서 수행됩니다. 단일 트랜잭션에 포함할 수 있는 이러한 작업의 수에는 제한이 없습니다. 데이터의 일관성을 유지하기 위해 트랜잭션은 포함된 모든 작업을 Datastore에 하나의 단위로 적용합니다. 즉, 작업이 하나라도 실패하면 어떠한 작업에도 적용되지 않습니다.

단일 트랜잭션 내의 항목을 대상으로 여러 작업을 수행할 수 있습니다. 예를 들어 객체의 카운터 필드를 증가시키려면 카운터 값을 읽고 새 값을 계산한 후 다시 저장해야 합니다. 트랜잭션을 사용하지 않으면 값을 읽은 후 업데이트하기 전에 다른 프로세스가 카운터를 증가시킬 수도 있으며, 이러한 경우 애플리케이션은 업데이트된 값을 덮어쓰게 됩니다. 단일 트랜잭션으로 읽기, 계산, 쓰기를 수행하면 값을 증가시킬 때 다른 프로세스가 방해할 수 없습니다.

트랜잭션 및 항목 그룹

트랜잭션 내에서는 상위 쿼리만 허용됩니다. 즉, 각 트랜잭션 쿼리는 단일 항목 그룹으로 제한되어야 합니다. 트랜잭션 자체는 여러 항목에 적용될 수 있습니다. 이러한 항목은 단일 항목 그룹에 속할 수도 있고, 교차 그룹 트랜잭션의 경우 최대 25개의 서로 다른 항목 그룹에 속할 수도 있습니다.

Datastore는 낙관적 동시 실행을 사용하여 트랜잭션을 관리합니다. 두 개 이상의 트랜잭션이 기존 항목을 업데이트하거나 새 항목을 생성하면서 동시에 같은 항목 그룹을 변경하려고 시도하면 가장 먼저 커밋하는 트랜잭션이 성공하고 다른 트랜잭션은 모두 커밋에 실패합니다. 다른 트랜잭션은 업데이트된 데이터를 대상으로 재시도할 수 있습니다. 이로 인해 특정 항목 그룹의 모든 항목에 대해 수행할 수 있는 동시 쓰기 수가 제한됩니다.

교차 그룹 트랜잭션

서로 다른 항목 그룹에 속하는 여러 항목에 대한 트랜잭션을 교차 그룹(XG) 트랜잭션이라고 합니다. 트랜잭션은 최대 25개의 항목 그룹에 걸쳐 적용될 수 있으며, 적용되는 항목 그룹 중 동시 트랜잭션이 발생하는 그룹이 하나도 없다면 트랜잭션이 성공합니다. 따라서 이질적인 여러 데이터를 같은 상위 항목에 속하도록 배치하지 않아도 원자적 쓰기를 수행할 수 있으므로 데이터를 보다 유연하게 구성할 수 있습니다.

단일 그룹 트랜잭션의 경우와 같이 XG 트랜잭션에서는 비상위 쿼리를 수행할 수 없습니다. 그러나 서로 다른 여러 항목 그룹을 대상으로 상위 쿼리를 수행할 수 있습니다. 트랜잭션이 아닌 비상위 쿼리는 이전에 커밋된 트랜잭션의 결과를 모두 인식하거나, 일부만 인식하거나, 전혀 인식하지 못할 수도 있습니다. (이 문제에 대한 자세한 내용은 Datastore 쓰기 및 데이터 가시성을 참조하세요.) 그러나 트랜잭션이 아닌 쿼리는 부분적으로 커밋된 단일 그룹 트랜잭션의 결과보다는 부분적으로 커밋된 XG 트랜잭션의 결과를 인식할 가능성이 더 높습니다.

단일 항목 그룹에만 적용되는 XG 트랜잭션은 단일 그룹에 대한 비XG 트랜잭션과 성능 및 비용이 동일합니다. 여러 항목 그룹에 적용되는 XG 트랜잭션의 작업 비용은 비XG 트랜잭션을 수행할 때와 동일하지만 지연 시간은 더 길 수 있습니다.

Datastore 쓰기 및 데이터 가시성

데이터는 두 단계를 거쳐 Datastore에 기록됩니다.

  1. 커밋 단계에서는 대다수 복제본의 트랜잭션 로그에 항목 데이터가 기록되고, 항목 데이터가 기록되지 않은 복제본은 최신 로그가 없는 것으로 표시됩니다.
  2. 적용 단계는 각 복제본에서 독립적으로 발생하며 두 가지 작업이 병렬로 수행됩니다.
    • 항목 데이터가 해당 복제본에 기록됩니다.
    • 항목의 색인 행이 해당 복제본에 기록됩니다. 데이터 자체를 기록할 때보다 시간이 더 오래 걸릴 수 있습니다.

쓰기 작업은 커밋 단계 직후에 반환됩니다. 적용 단계는 비동기식으로 진행되며, 복제본마다 서로 다른 시간에 진행될 수 있고 커밋 단계가 완료된 후 수백 밀리초 이상 지연될 수 있습니다. 커밋 단계 중에 오류가 발생하면 자동으로 재시도하지만, 오류가 계속되면 Datastore에서 오류 메시지를 반환하고 애플리케이션은 이 오류를 예외로 수신합니다. 커밋 단계는 성공했지만 특정 복제본에서 적용에 실패한 경우 다음 중 하나가 발생할 때 적용 단계가 완료로 롤포워드됩니다.

  • 주기적인 Datastore 정리 작업 시 미완료 커밋 작업을 확인하여 적용합니다.
  • 영향을 받는 항목 그룹을 사용하는 특정 작업(get, put, delete, 상위 쿼리)에서 새 작업을 진행하기 전에 수행한 변경사항이 커밋되었으나 해당 작업이 실행되는 복제본에 적용이 완료되지 않았습니다.

이 쓰기 동작은 커밋 단계와 적용 단계의 여러 부분에서 애플리케이션에 데이터가 표시되는 시점과 방식에 여러 가지 영향을 줄 수 있습니다.

  • 쓰기 작업이 제한 시간 오류를 보고하는 경우 데이터 읽기를 시도하지 않으면 작업의 성공 여부를 확인할 수 없습니다.
  • Datastore 가져오기 및 상위 쿼리는 해당 쿼리가 실행되는 복제본의 미완료 수정사항을 모두 적용하므로, 이러한 작업은 이전에 성공한 모든 트랜잭션을 항상 일관적으로 인식합니다. 즉, 업데이트된 항목을 키로 조회하는 get 작업은 항상 해당 항목의 최신 버전을 표시합니다.
  • 비상위 쿼리는 최신 트랜잭션이 아직 적용되지 않은 복제본에서 실행될 수도 있으므로 오래된 결과를 반환할 수 있습니다. 쿼리가 이전 작업과 다른 복제본에서 실행될 수 있으므로 미완료 트랜잭션을 항상 적용하는 작업을 수행했더라도 이 문제가 발생할 수 있습니다.
  • 동시 변경의 타이밍이 비상위 쿼리의 결과에 영향을 줄 수 있습니다. 항목이 처음에는 쿼리를 충족했지만 이후에 변경되어 쿼리를 충족하지 못하는 경우, 쿼리가 실행된 복제본의 색인에 변경사항이 아직 적용되지 않았다면 쿼리의 결과 집합에 해당 항목이 여전히 포함될 수 있습니다.

Datastore 통계

Datastore는 지정된 종류의 항목 수 또는 지정된 유형의 속성 값에서 사용된 공간 등 애플리케이션에 저장된 데이터에 대한 통계를 유지합니다. Google Cloud 콘솔 Datastore 대시보드 페이지에서 이러한 통계를 볼 수 있습니다. 또한 애플리케이션에서 Datastore API를 사용하여 특정 항목 이름을 쿼리하면 프로그래매틱 방식으로 이러한 값에 액세스할 수 있습니다. 자세한 내용은 Python 2의 Datastore 통계를 참조하세요.

Python 2 Datastore 예시

Python API에서 모델은 속성의 유형 및 구성을 포함한 항목의 종류를 설명합니다. 애플리케이션은 Python 클래스를 사용하여 모델을 정의하며 클래스 특성으로 속성을 설명합니다. 클래스의 이름은 항목 종류의 이름이 됩니다. 지정된 종류의 항목은 모델 클래스의 인스턴스로 표현되며 인스턴스 특성은 속성 값을 나타냅니다. 새 항목을 만들려면 원하는 클래스의 객체를 만들고 특성을 설정한 다음 put()과 같은 메서드를 호출하여 저장합니다.

import datetime
from google.appengine.ext import db
from google.appengine.api import users

class Employee(db.Model):
  name = db.StringProperty(required=True)
  role = db.StringProperty(required=True,
                           choices=set(["executive", "manager", "producer"]))
  hire_date = db.DateProperty()
  new_hire_training_completed = db.BooleanProperty(indexed=False)
  email = db.StringProperty()

e = Employee(name="John",
             role="manager",
             email=users.get_current_user().email())
e.hire_date = datetime.datetime.now().date()
e.put()

Datastore API는 쿼리를 위한 두 가지 인터페이스로 쿼리 객체 인터페이스, 그리고 GQL이라는 SQL과 비슷한 쿼리 언어를 제공합니다. 쿼리는 모델 클래스 인스턴스의 형태로 항목을 반환합니다.

training_registration_list = ["Alfred.Smith@example.com",
                              "jharrison@example.com",
                              "budnelson@example.com"]
employees_trained = db.GqlQuery("SELECT * FROM Employee WHERE email IN :1",
                                training_registration_list)
for e in employees_trained:
  e.new_hire_training_completed = True
  db.put(e)