항목, 속성, 키

Datastore의 데이터 객체를 항목이라고 합니다. 항목에는 명명된 속성이 한 개 이상 있으며 각 속성에는 값이 한 개 이상 있을 수 있습니다. 같은 종류의 항목이라도 속성이 동일할 필요는 없으며, 항목의 특정 속성 값이 모두 동일한 데이터 유형이 아니어도 됩니다. 필요한 경우 애플리케이션이 자체 데이터 모델에 이러한 제한을 설정하고 시행할 수 있습니다.

Datastore는 다양한 속성 값 데이터 유형을 지원합니다. 여기에는 다음이 포함됩니다.

  • 정수
  • 부동 소수점 수
  • 문자열
  • 날짜
  • 바이너리 데이터

전체 유형 목록은 속성 및 값 유형을 참조하세요.

Datastore의 각 항목에는 항목을 고유하게 식별하는 가 있습니다. 키는 다음 구성요소로 구성됩니다.

  • 멀티테넌시를 허용하는 항목의 네임스페이스
  • Datastore 쿼리 목적으로 항목을 분류하는 항목의 종류
  • 다음 중 하나에 해당하는 개별 항목의 식별자
    • 키 이름 문자열
    • 정수 숫자 ID
  • Datastore 계층 구조에서 항목 위치를 나타내는 선택적 상위 경로

애플리케이션은 항목의 키를 사용하여 Datastore에서·개별 항목을 가져오거나 항목 키 또는 속성 값에 따라 쿼리를 실행하여 항목을 한 개 이상 검색할 수 있습니다.

Go App Engine SDK에는 Datastore 항목을 Go 구조체로 나타내고, 이 항목을 Datastore에서 저장하고 검색하도록 지원하는 패키지가 포함되어 있습니다.

Datastore 자체는 지정된 속성에 특정 유형의 값이 있는지 여부와 같은 항목 구조에 대한 제한을 적용하지 않습니다. 이 태스크는 애플리케이션에서 수행됩니다.

종류 및 식별자

각 Datastore 항목은 쿼리 목적으로 항목을 분류하는 특정 종류에 속합니다. 예를 들어 인사 관리 애플리케이션은 회사의 각 직원을 종류가 Employee인 항목으로 나타낼 수 있습니다. Go Datastore API에서는 datastore.Key를 만들 때 항목의 종류를 지정합니다. 밑줄(__) 두 개로 시작하는 종류 이름은 모두 예약되어 있어 이를 사용할 수 없습니다.

다음 예시에서는 Employee 종류의 항목을 만들고 속성 값을 채우고 Datastore에 저장합니다.

import (
	"context"
	"time"

	"google.golang.org/appengine/datastore"
)

type Employee struct {
	FirstName          string
	LastName           string
	HireDate           time.Time
	AttendedHRTraining bool
}

func f(ctx context.Context) {
	// ...
	employee := &Employee{
		FirstName: "Antonio",
		LastName:  "Salieri",
		HireDate:  time.Now(),
	}
	employee.AttendedHRTraining = true

	key := datastore.NewIncompleteKey(ctx, "Employee", nil)
	if _, err := datastore.Put(ctx, key, employee); err != nil {
		// Handle err
	}
	// ...
}

Employee 유형은 데이터 모델에 FirstName, LastName, HireDate, AttendedHRTraining이라는 4개의 필드를 선언합니다.

종류 외에도 각 항목에는 항목이 생성될 때 할당된 식별자가 있습니다. 식별자는 항목 키의 일부이므로 항목과 영구적으로 연관되며 변경할 수 없습니다. 식별자를 할당할 수 있는 두 가지 방법은 다음과 같습니다.

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

항목에 키 이름을 할당하려면 datastore.NewKey에 비어 있지 않은 stringID 인수를 제공합니다.

// Create a key with a key name "asalieri".
key := datastore.NewKey(
	ctx,        // context.Context
	"Employee", // Kind
	"asalieri", // String ID; empty means no string ID
	0,          // Integer ID; if 0, generate automatically. Ignored if string ID specified.
	nil,        // Parent Key; nil means no parent
)

Datastore에서 숫자 ID를 자동으로 할당하도록 하려면 빈 stringID 인수를 사용합니다.

// Create a key such as Employee:8261.
key := datastore.NewKey(ctx, "Employee", "", 0, nil)
// This is equivalent:
key = datastore.NewIncompleteKey(ctx, "Employee", nil)

식별자 할당

두 가지 자동 ID 정책을 사용하여 자동으로 ID를 생성하도록 Datastore를 구성할 수 있습니다.

  • default 정책은 거의 균일하게 분포한 미사용 ID의 임의 시퀀스를 생성합니다. 각 ID는 최대 16자리 숫자입니다.
  • legacy 정책은 연속되지 않는 더 작은 정수 ID 시퀀스를 만듭니다.

사용자에게 항목 ID를 표시하거나 항목 ID의 순서를 결정하려는 경우 수동으로 할당하는 것이 좋습니다.

Datastore는 거의 균일하게 분포된 임의 순서의 미사용 ID를 생성합니다. 각 ID는 최대 16자리 숫자입니다.

상위 경로

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

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

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

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

[Person:GreatGrandpa]

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

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

항목의 상위 항목을 지정하려면 datastore.NewKeyparent 인수를 사용합니다. 이 인수의 값은 상위 항목의 키여야 합니다. 다음 예시에서는 Address 종류의 항목을 만들고 Employee 항목을 해당 상위 항목으로 지정합니다.

// Create Employee entity
employee := &Employee{ /* ... */ }
employeeKey, err := datastore.Put(ctx, datastore.NewIncompleteKey(ctx, "Employee", nil), employee)

// Use Employee as Address entity's parent
// and save Address entity to datastore
address := &Address{ /* ... */ }
addressKey := datastore.NewIncompleteKey(ctx, "Address", employeeKey)
_, err = datastore.Put(ctx, addressKey, address)

트랜잭션 및 항목 그룹

항목을 생성, 업데이트 또는 삭제하려는 모든 시도는 트랜잭션의 관점에서 수행됩니다. 단일 트랜잭션에 포함할 수 있는 이러한 작업의 수에는 제한이 없습니다. 데이터의 일관성을 유지하기 위해 트랜잭션은 포함된 모든 작업을 Datastore에 하나의 단위로 적용합니다. 즉, 작업이 하나라도 실패하면 어떠한 작업에도 적용되지 않습니다. 또한 동일 트랜잭션 내에서 수행되는 모든 strong consistency를 가지는 읽기(상위 쿼리 또는 get)에서 데이터의 일관된 스냅샷이 관찰됩니다.

위에 언급한 대로 항목 그룹은 상위 항목을 통해 공통 루트 요소로 연결되는 항목 집합입니다. 데이터를 항목 그룹으로 구성하면 수행 가능한 트랜잭션을 제한할 수 있습니다.

  • 트랜잭션에서 액세스하는 모든 데이터는 최대 25개의 항목 그룹에만 포함될 수 있습니다.
  • 트랜잭션 내에서 쿼리를 사용하려면 올바른 데이터에 일치하는 상위 필터를 지정할 수 있는 방식으로 데이터가 항목 그룹으로 구성되어 있어야 합니다.
  • 단일 항목 그룹 내에서는 쓰기 처리량 한도가 초당 약 1개의 트랜잭션으로 제한되어 있습니다. 이 한도가 존재하는 이유는 Datastore가 높은 안정성과 내결함성을 제공하기 위해 광범위한 지리적 영역에 걸쳐 각 항목 그룹의 마스터 없는 동기 복제 작업을 수행하기 때문입니다.

많은 애플리케이션에서 관련성이 없는 데이터를 포괄적으로 확인할 때는 eventual consistency(즉, 여러 항목 그룹에 걸친 비상위 쿼리로 약간 오래된 데이터를 반환할 수 있음)를 사용하고 관련성이 높은 단일 데이터 세트를 보거나 수정할 때는 strong consistency(상위 쿼리 또는 단일 항목의 get)를 사용하는 것이 좋을 수 있습니다. 이러한 애플리케이션에서는 일반적으로 관련성이 높은 각 데이터 세트마다 별도의 항목 그룹을 사용하는 방식이 바람직합니다. 자세한 내용은 strong consistency를 위한 구조화를 참조하세요.

속성 및 값 유형

항목과 연관된 데이터 값은 하나 이상의 속성으로 구성됩니다. 각 속성에는 이름과 하나 이상의 값이 있습니다. 속성에는 둘 이상의 유형 값이 있을 수 있으며, 두 개의 항목에 동일한 속성의 서로 다른 유형 값이 있을 수 있습니다. 속성은 색인 생성되거나 색인 생성되지 않을 수 있으며, 속성 P를 기준으로 정렬 또는 필터링하는 쿼리는 P가 색인 생성되지 않은 항목을 무시합니다. 항목 하나에 포함될 수 있는 색인 생성된 속성은 최대 20,000개입니다.

지원되는 값 유형은 다음과 같습니다.

값 유형 Go 유형 정렬 순서 참고
정수 int
int8
int16
int32
int64
숫자 부호 있는 64비트 정수
부동 소수점 수 float32
float64
숫자 64비트 배정밀도,
IEEE 754
불리언 bool false<true
문자열(short) string 유니코드
최대 1500바이트. 값이 1500바이트보다 크면 런타임 시 오류 발생
문자열(long) string(noindex 포함) 없음 최대 1MB

색인 생성되지 않음
바이트 슬라이스(short) datastore.ByteString 바이트순 최대 1500바이트. 값이 1500바이트보다 크면 런타임 시 오류 발생
바이트 슬라이스(long) []byte 없음 최대 1MB

색인 생성되지 않음
날짜 및 시간 time.Time 시간순
지리적 지점 appengine.GeoPoint 위도순 우선 적용
후 경도순
Datastore 키 *datastore.Key 경로 요소 기준
(종류, 식별자,
종류, 식별자...)
Blobstore 키 appengine.BlobKey 바이트순

struct 또는 slice를 사용하여 속성을 집계할 수도 있습니다. 자세한 내용은 Datastore 참조를 확인하세요.

쿼리에 혼합 유형 값이 있는 필드가 있으면 Datastore는 내부 표시를 기준으로 확정된 순서를 사용합니다.

  1. Null 값
  2. 고정 소수점 수
    • 정수
    • 날짜 및 시간
  3. 부울 값
  4. 바이트 시퀀스
    • 바이트 슬라이스(short)
    • 유니코드 문자열
    • Blobstore 키
  5. 부동 소수점 수
  6. 지리적 지점
  7. Datastore 키

long 바이트 슬라이스 및 long 문자열은 색인이 생성되지 않기 때문에 순서가 정의되지 않습니다.

항목 작업

애플리케이션은 Datastore API를 사용하여 항목을 생성, 검색, 업데이트, 삭제할 수 있습니다. 애플리케이션이 항목의 전체 키를 알고 있거나 상위 키, 종류, 식별자에서 파생시킬 수 있는 경우 키를 사용하여 항목에서 직접 작동할 수 있습니다. 또한 애플리케이션은 Datastore 쿼리의 결과로 항목 키를 가져올 수 있습니다. 자세한 내용은 Datastore 쿼리 페이지를 참조하세요.

항목 생성

Go에서는 Go 구조체의 인스턴스를 구성하고 해당 필드를 채우고, datastore.Put을 호출하여 Datastore에 저장하는 방식으로 새 항목을 만듭니다. 내보낸 필드(대문자로 시작)만 Datastore에 저장됩니다. 비어 있지 않은 stringID 인수를 datastore.NewKey에 전달하여 항목의 키 이름을 지정할 수 있습니다.

employee := &Employee{
	FirstName: "Antonio",
	LastName:  "Salieri",
	HireDate:  time.Now(),
}
employee.AttendedHRTraining = true
key := datastore.NewKey(ctx, "Employee", "asalieri", 0, nil)
_, err = datastore.Put(ctx, key, employee)

비어 있는 키 이름을 제공하거나 datastore.NewIncompleteKey를 사용하면 Datastore가 항목 키의 숫자 ID를 자동으로 생성합니다.

employee := &Employee{
	FirstName: "Antonio",
	LastName:  "Salieri",
	HireDate:  time.Now(),
}
employee.AttendedHRTraining = true
key := datastore.NewIncompleteKey(ctx, "Employee", nil)
_, err = datastore.Put(ctx, key, employee)

항목 검색

특정 키로 식별되는 항목을 검색하려면 *datastore.Keydatastore.Get 함수에 인수로 전달합니다. datastore.NewKey 함수를 사용하여 *datastore.Key를 생성할 수 있습니다.

employeeKey := datastore.NewKey(ctx, "Employee", "asalieri", 0, nil)
addressKey := datastore.NewKey(ctx, "Address", "", 1, employeeKey)
var addr Address
err = datastore.Get(ctx, addressKey, &addr)

datastore.Get이 해당하는 Go 구조체의 인스턴스를 채웁니다.

항목 업데이트

기존 항목을 업데이트하려면 구조체의 속성을 수정한 후 datastore.Put을 호출합니다. 데이터가 기존 항목을 덮어씁니다. datastore.Put을 호출할 때마다 전체 객체가 Datastore로 전송됩니다.

항목 삭제

항목의 키가 제공되면 datastore.Delete 함수를 사용하여 항목을 삭제할 수 있습니다.

key := datastore.NewKey(ctx, "Employee", "asalieri", 0, nil)
err = datastore.Delete(ctx, key)

일괄 작업

datastore.Put, datastore.Get, datastore.Delete에는 datastore.PutMulti, datastore.GetMulti, datastore.DeleteMulti라는 일괄 변형이 있습니다. 단일 Datastore 호출에서 여러 항목에 대한 작업을 허용합니다.

// A batch put.
_, err = datastore.PutMulti(ctx, []*datastore.Key{k1, k2, k3}, []interface{}{e1, e2, e3})

// A batch get.
var entities = make([]*T, 3)
err = datastore.GetMulti(ctx, []*datastore.Key{k1, k2, k3}, entities)

// A batch delete.
err = datastore.DeleteMulti(ctx, []*datastore.Key{k1, k2, k3})

일괄 작업을 수행해도 비용은 변하지 않습니다. 각 키가 있는지 여부와 관계없이 일괄 처리된 작업에서 모든 키에 요금이 부과됩니다. 작업과 관련된 항목 크기는 비용에 영향을 주지 않습니다.