권장사항

여기에 나열된 권장사항을 참조하여 Cloud Datastore를 사용하는 애플리케이션을 빌드할 때 유의할 점을 빠르게 확인할 수 있습니다. 이 페이지는 Cloud Datastore의 기본적인 사용법을 안내하지 않으므로 Cloud Datastore를 시작한 지 얼마 안 되는 사용자에게는 적합하지 않을 수 있습니다. 신규 사용자라면 Cloud Datastore 시작하기부터 살펴보는 것이 좋습니다.

일반

  • 네임스페이스 이름, 종류 이름, 속성 이름, 커스텀 키 이름에는 항상 UTF-8 문자를 사용하세요. 이러한 이름에 UTF-8 이외의 문자를 사용하면 Cloud Datastore 기능에 지장을 줄 수 있습니다. 예를 들어 속성 이름에 UTF-8 이외의 문자를 사용하면 속성을 사용하는 색인이 생성되지 않을 수 있습니다.
  • 종류 이름 또는 커스텀 키 이름에 슬래시(/)를 사용하지 마세요. 이러한 이름에 슬래시를 사용하면 추후 기능을 사용하는 데 지장이 있을 수 있습니다.
  • Cloud Project ID에 민감한 정보를 저장하지 않도록 유의하세요. Cloud Project ID는 프로젝트 수명 이후에도 보존될 수 있습니다.

API 호출

  • 읽기, 쓰기, 삭제 시 단일 작업 대신 일괄 작업을 사용하세요. 일괄 작업은 단일 작업과 동일한 오버헤드로 여러 작업을 수행하기 때문에 보다 효율적입니다.
  • 트랜잭션이 실패하면 트랜잭션 롤백을 시도하세요. 롤백은 트랜잭션 중인 동일한 리소스를 두고 경쟁하는 다른 요청의 재시도 지연 시간을 최소화합니다. 롤백 자체도 실패할 수 있으므로 롤백을 최선을 위한 시도로만 수행해야 합니다.
  • 해당되는 경우 동기식 호출 대신 비동기식 호출을 사용하세요. 비동기식 호출은 지연 영향을 최소화합니다. 예를 들어 응답을 렌더링하기 위해 동기식 lookup()의 결과와 쿼리의 결과를 필요로 하는 애플리케이션을 생각해 보세요. lookup() 및 쿼리에 데이터 종속성이 없다면 쿼리를 실행하기 전에 lookup()이 완료될 때까지 동기식으로 대기할 필요가 없습니다.

항목

  • 관련성이 높은 데이터는 항목 그룹으로 그룹화하세요. 항목 그룹은 상위 쿼리를 사용하여 강력한 일관성을 갖춘 결과를 반환합니다. 또한 항목 그룹의 항목은 Cloud Datastore 서버에서 물리적으로 가깝게 저장되기 때문에 상위 쿼리를 수행하면 최소한의 I/O로 항목 그룹을 신속하게 스캔할 수 있습니다.
  • 항목 그룹 쓰기 속도가 초당 1회를 넘지 않도록 유의하세요. 쓰기 속도가 지속적으로 이 한도보다 빠르면 읽기의 최종 일관성을 확보하는 시간이 지연되어 일관성이 강력한 읽기 시간이 초과되고 애플리케이션의 전반적인 성능이 낮아지는 결과로 이어집니다. 항목 그룹의 배치 또는 트랜잭션 쓰기는 이 한도에서 단일 쓰기로 집계됩니다.
  • 동일한 항목(키별)을 동일한 커밋에 여러 번 포함하지 마세요. 동일한 항목을 동일한 커밋에 여러 번 포함하면 Cloud Datastore 지연 시간에 영향을 줄 수 있습니다.

  • 키 이름을 항목 생성 시 제공하지 않으면 자동으로 생성됩니다. 이름은 키스페이스에 균등하게 분산되도록 할당됩니다.
  • 커스텀 이름을 사용하는 키의 경우에는 항상 슬래시(/)를 제외하고 UTF-8 문자를 사용하세요. UTF-8 이외의 문자는 Google BigQuery로 Cloud Datastore 백업을 가져오는 등의 여러 프로세스에 지장을 줍니다. 슬래시는 추후 기능에 지장을 줄 수 있습니다.
  • 숫자 ID를 사용하는 키의 경우:
    • ID에 음수를 사용하지 마세요. 음수 ID는 정렬에 지장을 줄 수 있습니다.
    • ID에 0(영) 값을 사용하지 마세요. 이 값을 사용하면 ID가 자동으로 할당됩니다.
    • 항목을 생성할 때 원하는 숫자 ID를 수동으로 할당하려면 allocateIds() 메소드를 통해 애플리케이션에 하나의 ID 블록을 할당하세요. 그러면 Cloud Datastore에서 수동 숫자 ID를 다른 항목에 할당하지 않습니다.
  • 항목을 생성할 때 직접 지정한 자체 숫자 ID 또는 커스텀 이름을 할당하려면 다음과 같은 단조 증가 값을 사용하지 마세요.

    1, 2, 3, …,
    "Customer1", "Customer2", "Customer3", ….
    "Product 1", "Product 2", "Product 3", ….
    

    애플리케이션에서 순차 번호 지정과 같은 대량의 트래픽이 발생하면 핫스팟이 Cloud Datastore 지연에 영향을 미치는 결과로 이어질 수 있습니다. 연속 숫자 ID 발급을 피하려면 allocateIds() 메소드로 번호 ID를 받으세요. allocateIds() 메소드는 숫자 ID가 적절하게 분포된 순서를 생성합니다.

  • 키를 지정하거나 생성된 이름을 저장하는 방식을 사용해도 항목을 찾는 쿼리를 생성할 필요 없이 나중에 해당 항목에 대한 일관된 lookup()을 수행할 수 있습니다.

색인

  • 속성이 쿼리에 필요하지 않은 경우 색인에서 속성을 제외하세요. 속성의 불필요한 색인은 일관성 확보 시간을 지연시키고 색인 항목의 저장 비용을 높이는 결과로 이어집니다.
  • 복합 색인이 지나치게 많지 않도록 하세요. 복합 색인의 과도한 사용은 일관성 확보 시간을 지연시키고 색인 항목의 저장 비용을 높이는 결과로 이어집니다. 사전 정의된 색인 없이 대량의 데이터세트에 대한 임시 쿼리를 실행해야 하는 경우 Google BigQuery를 사용하세요.
  • 단조 증가 값(예: NOW() 타임스탬프)으로 속성을 색인하지 마세요. 이러한 색인을 유지하면 핫스팟이 발생해 읽기 및 쓰기 속도가 높은 애플리케이션에 대해 Cloud Datastore가 지연되는 영향을 미칠 수 있습니다. 단조로운 속성을 처리하는 방법에 대한 자세한 내용은 아래에 있는 좁은 키 범위에 대한 높은 읽기/쓰기 속도를 참조하세요.

속성

  • 문자열 유형의 속성에는 항상 UTF-8 문자를 사용하세요. 문자열 유형의 속성에 UTF-8 이외의 문자를 사용하면 쿼리에 지장을 줄 수 있습니다. UTF-8 이외의 문자가 포함된 데이터를 저장해야 하는 경우에는 바이트 문자열을 사용하세요.
  • 속성 이름에 마침표를 사용하지 마세요. 속성 이름에 마침표를 사용하면 이후 구조화된 속성을 색인하는 데 지장을 줄 수 있습니다.

쿼리

  • 쿼리 결과 중 키에만 액세스하면 되는 경우에는 키 전용 쿼리를 사용하세요. 키 전용 쿼리는 전체 항목을 검색하는 것보다 낮은 지연 시간과 비용으로 결과를 반환합니다.
  • 항목에서 특정 속성에만 액세스하면 되는 경우에는 프로젝션 쿼리를 사용하세요. 프로젝션 쿼리는 전체 항목을 검색하는 것보다 낮은 지연 시간과 비용으로 결과를 반환합니다.
  • 마찬가지로 쿼리 필터에 포함된 속성(예: order by 절에 나열된 속성)에만 액세스하면 되는 경우에는 프로젝션 쿼리를 사용하세요.
  • 오프셋 대신 커서를 사용하세요. 오프셋을 사용하면 건너뛴 항목이 애플리케이션에 반환되는 것을 방지할 수 있지만 이러한 항목은 여전히 내부적으로 검색됩니다. 건너뛴 항목은 쿼리의 지연 시간에 영향을 미치며 검색에 필요한 읽기 작업에 해당되는 비용이 애플리케이션에 청구됩니다.
  • 쿼리에 강력한 일관성이 필요한 경우에는 상위 쿼리를 사용하세요. 상위 쿼리를 사용하려면 먼저 강력한 일관성을 갖추도록 데이터를 구조화해야 합니다. 상위 쿼리는 일관성이 강력한 결과를 반환합니다. 비상위 키 전용 쿼리에 이어 lookup()을 수행하면 강력한 결과가 반환되지 않습니다. 비상위 키 전용 쿼리는 쿼리 당시 일관되지 않은 색인에서 결과를 가져올 수 있기 때문입니다.

규모 확장을 위한 설계

단일 항목 그룹 업데이트

Cloud Datastore의 단일 항목 그룹 업데이트 속도를 너무 높게 지정하면 안 됩니다.

Google은 Cloud Datastore를 사용하는 경우 초당 1회가 넘는 속도로 항목 그룹을 업데이트할 필요가 없도록 애플리케이션을 설계할 것을 권장합니다. 상위 및 하위 계층이 없는 항목은 그 자체가 항목 그룹이라는 사실을 기억하세요. 항목 그룹을 너무 빨리 업데이트하면 Cloud Datastore 쓰기의 지연 시간, 제한시간 및 기타 유형의 오류가 늘어납니다. 이를 경합이라고 부릅니다.

단일 항목 그룹에 대한 Cloud Datastore 쓰기 속도가 초당 1회 한도를 넘어도 로드 테스트에서는 이 문제가 드러나지 않을 수도 있습니다. 항목 그룹에 대한 쓰기 속도를 낮추도록 애플리케이션을 설계하는 방법에 대한 일부 권장 사항이 Cloud Datastore 경합 문서에 나와 있습니다.

좁은 키 범위에 대한 높은 읽기/쓰기 속도

사전순으로 가까운 Cloud Datastore 키에 대한 읽기 또는 쓰기 속도가 높아지지 않도록 유의하세요.

Cloud Datastore는 Google의 NoSQL 데이터베이스인 Bigtable을 기반으로 하므로 Bigtable 성능 특성의 영향을 받습니다. Bigtable은 분리된 태블릿으로 행을 샤딩하는 방식으로 확장되며 이러한 행은 키를 기준으로 사전순 정렬됩니다.

Cloud Datastore를 사용하는 경우, 작은 범위의 키에 대한 쓰기 속도가 급증하여 단일 태블릿 서버의 용량을 초과하면 핫 태블릿으로 인해 쓰기 속도가 느려질 수 있습니다. Bigtable은 높은 로드를 지원하기 위해 키 공간을 결국 분할하게 됩니다.

단일 키를 매우 높은 속도로 읽는 경우를 제외하고 읽기 한도는 일반적으로 쓰기 한도보다 높습니다. Bigtable이 단일 키를 2개 이상의 태블릿으로 분할하는 것은 불가능합니다.

핫 태블릿은 항목 키와 색인에 모두 사용되는 키 범위에 적용할 수 있습니다.

경우에 따라 좁은 범위의 키에 대한 읽기 또는 쓰기를 방지하는 것보다 Cloud Datastore 핫스팟이 애플리케이션에 더 큰 영향을 미칠 수 있습니다. 예를 들어 인스턴스를 시작하는 동안 핫 키에 대한 읽기 또는 쓰기가 이루어지면 로드 요청이 실패할 수 있습니다.

기본적으로 Cloud Datastore는 분산형 알고리즘을 사용하여 키를 할당합니다. 따라서 기본 ID 할당 정책을 사용하여 높은 쓰기 속도로 신규 항목을 생성하면 일반적으로는 Cloud Datastore 쓰기에 핫스팟이 발생하지 않습니다. 이러한 문제가 발생하는 일부 특수한 상황은 다음과 같습니다.

  • 기존의 연속 ID 할당 정책을 사용하여 매우 높은 속도로 신규 항목을 생성하는 경우

  • 매우 높은 속도로 신규 항목을 생성하면서 단조 증가하는 자체 ID를 할당하는 경우

  • 예전에 기존 항목이 거의 없었던 종류의 신규 항목을 매우 높은 속도로 생성하는 경우. Bigtable은 모든 항목이 동일한 태블릿 서버에 있는 상태로 시작하며 키 범위를 분리된 태블릿 서버로 분할하는 데 다소 시간이 소요됩니다.

  • 또한 타임스탬프처럼 색인 속성이 단조 증가하는 신규 항목을 높은 속도로 생성하는 경우에도 이 문제가 나타납니다. 이러한 속성은 Bigtable에서 색인 테이블 행의 키이기 때문입니다.

  • Cloud Datastore가 Bigtable 행 키의 앞에 루트 항목 그룹의 네임스페이스와 종류를 추가하는 경우. 점진적으로 트래픽을 늘리지 않고 신규 네임스페이스 또는 종류에 쓰기를 시작하면 핫스팟이 발생할 수 있습니다.

키 또는 색인 생성된 속성이 단조 증가한다면 임의의 해시를 앞에 추가하여 키가 여러 태블릿으로 샤딩되도록 만들 수 있습니다.

마찬가지로 정렬이나 필터를 사용하여 단조 증가(또는 감소)하는 속성을 쿼리해야 한다면 새 속성의 색인을 생성하는 방법을 대신 사용할 수 있습니다. 이 경우 데이터세트 전체에서 카디널리티가 높지만 수행하려는 쿼리 범위 내의 모든 항목에 공통된 값을 단조 값 앞에 추가합니다. 예를 들어 타임스탬프를 기준으로 항목을 쿼리하려고 하지만 한 번에 한 명의 사용자에 대한 결과만 필요하다면 타임스탬프 앞에 사용자 ID를 추가하고 이 신규 속성의 색인을 생성하면 됩니다. 이렇게 하면 사용자에 대한 쿼리 및 결과 정렬이 여전히 가능할 뿐 아니라 사용자 ID가 있기 때문에 색인 자체가 적절히 샤딩됩니다.

이 문제에 대한 자세한 설명을 살펴보려면 Cloud Datastore에서 단조 증가하는 값을 저장하는 방법에 대한 Ikai Lan의 블로그 게시물을 참조하세요.

트래픽 늘리기

키스페이스의 신규 Cloud Datastore 종류 또는 부분에 대한 트래픽을 점진적으로 늘리세요.

Bigtable이 트래픽 증가에 따라 태블릿을 분할할 수 있는 시간을 충분히 확보하려면 새로운 Cloud Datastore 종류에 대한 트래픽을 점진적으로 늘려야 합니다. 새로운 Cloud Datastore 종류인 경우에는 최대 작업 수를 초당 500개로 제한하고 5분마다 50%씩 트래픽을 늘리는 것이 좋습니다. 이 증가 일정에 따르면 이론상으로 90분 후에는 초당 작업 수를 74만 개로 늘릴 수 있습니다. 쓰기 작업이 키 범위 전반에 걸쳐 비교적 균등하게 분산되도록 만드세요. Google SRE는 이 법칙을 '500/50/5' 법칙이라고 부릅니다.

이러한 점진적인 증가 패턴은 A 종류의 사용을 중단하고 대신 B 종류를 사용하도록 코드를 변경하는 경우에 특히 중요합니다. 단편적으로 보면 A 종류를 읽어 A 종류가 없으면 B 종류를 읽도록 코드를 변경하면 이 마이그레이션을 처리할 수 있다고 생각할 수도 있습니다. 그러나 이렇게 하면 키스페이스 영역이 매우 적어지기 때문에 새로운 종류에 트래픽이 급증하는 원인이 될 수 있습니다. 키스페이스가 부족하면 Bigtable이 효과적으로 태블릿을 분할하지 못할 수도 있습니다.

동일한 종류 내에서 다양한 범위의 키를 사용하도록 항목을 마이그레이션하는 경우에도 동일한 문제가 발생할 수 있습니다.

새로운 종류 또는 키로 항목을 마이그레이션하기 위해 사용하는 전략은 데이터 모델에 따라 달라집니다. 아래에서는 '병렬 읽기'로 알려진 예제 전략을 소개합니다. 이 전략이 내 데이터에 효과가 있는지 여부는 스스로 판단해야 합니다. 마이그레이션 도중 병렬 작업이 비용에 미치는 영향을 중요하게 고려하세요.

우선 기존 항목 또는 키를 읽습니다. 기존 항목이 없는 경우에는 신규 항목이나 키에서 읽을 수 있습니다. 부재 항목 읽기 속도가 높으면 핫스팟으로 이어질 수 있으므로 점차적으로 로드를 늘려야 합니다. 기존 항목을 신규 항목에 복사한 후 기존 항목을 삭제하는 전략이 바람직합니다. 새로운 키스페이스가 적절히 분산되도록 병렬 읽기를 점차적으로 증가하세요.

신규 종류에 대한 읽기 또는 쓰기를 점차적으로 늘리려면 임의 비율의 사용자가 신규 항목에 쓰기 작업을 수행할 수 있도록 사용자 ID의 확정 해시를 사용하는 전략을 사용할 수 있습니다. 사용자 ID 해시의 결과가 임의 기능 또는 사용자 행동에 의해 왜곡되지 않았는지 확인하세요.

그동안 Dataflow 작업을 실행하여 기존 항목 또는 키의 모든 데이터를 신규 항목 또는 키에 복사하세요. 일괄 작업 수행 시 Bigtable 핫스팟을 방지하려면 연속 키에 쓰기 작업을 수행하면 안 됩니다. 일괄 작업이 완료되면 새로운 위치에서만 읽기가 가능해집니다.

이 전략을 개선하려면 사용자를 한 번에 소량 배치씩 마이그레이션하면 됩니다. 사용자 항목에 해당 사용자의 마이그레이션 상태를 추적하는 필드를 추가하고, 사용자 ID의 해시에 근거하여 마이그레이션할 사용자 배치를 선택하세요. 그러면 Mapreduce 또는 Dataflow 작업이 해당 배치의 사용자를 위한 키를 마이그레이션합니다. 이때 마이그레이션이 진행 중인 사용자는 병렬 읽기를 사용하게 됩니다.

마이그레이션이 진행되는 동안 기존 및 신규 항목 모두에 이중 쓰기를 수행하지 않으면 롤백을 쉽게 수행할 수 없으며, Cloud Datastore 비용이 증가하는 원인이 됩니다.

삭제

작은 범위의 키에서 대량의 Cloud Datastore 항목을 삭제하지 않도록 유의하세요.

Bigtable은 삭제된 항목을 없애고, 읽기 및 쓰기 효율성이 높아지도록 데이터를 재구성하기 위해 테이블을 주기적으로 재작성합니다. 이 프로세스를 압축이라고 부릅니다.

작은 범위의 키에서 대량의 Cloud Datastore 항목을 삭제하면 압축이 완료될 때까지 이 부분의 색인에 대한 쿼리 속도가 느려집니다. 최악의 경우에는 결과를 반환하기 전에 쿼리 시간이 초과될 수 있습니다.

항목의 만료 시간을 나타내기 위해 색인된 필드에 타임스탬프 값을 사용하는 이 패턴은 되도록 피해야 합니다. 만료된 항목을 검색하려면 이 색인 필드에 대해 쿼리를 수행해야 합니다. 이 필드는 키스페이스의 일부가 가장 최근 삭제한 항목의 색인 항목과 겹친 상태로 상주할 가능성이 높습니다.

만료 타임스탬프 앞에 고정 길이 문자열을 추가하는 '분할 쿼리'를 실행하면 성능을 향상시킬 수 있습니다. 동일한 타임스탬프의 항목이 색인의 키 범위에 걸쳐 검색되도록 전체 문자열에 대해 색인이 정렬됩니다. 각 분할에서 결과를 가져오려면 여러 쿼리를 병행해서 실행하세요.

만료 타임스탬프 문제를 보다 만족스럽게 해결하는 방법은 주기적으로 업데이트되는 글로벌 카운터인 '세대 번호'를 사용하는 것입니다. 세대 번호는 만료 타임스탬프 앞에 추가되므로 쿼리는 세대 번호, 분할, 타임스탬프 순의 기준으로 정렬됩니다. 기존 항목의 삭제는 이전 세대에서 이루어집니다. 삭제되지 않은 모든 항목의 세대 번호는 증분되어야 합니다. 삭제가 완료되면 다음 세대로 이동하세요. 압축이 완료될 때까지 이전 세대에 대한 쿼리는 저조한 성능을 보입니다. 최종 일관성으로 인한 결과 누락의 위험성을 줄이려면 삭제할 항목 목록을 가져오는 색인 쿼리가 이루어지기 전에 여러 세대가 완료되기를 기다려야 할 수 있습니다.

분할 및 복제

핫 Cloud Datastore 키에는 분할이나 복제를 사용하세요.

Bigtable에서 허용하는 것보다 높은 속도로 키 범위 영역을 읽어야 한다면 복제를 사용하면 됩니다. 이 전략을 사용하면 동일한 항목의 N개 사본을 저장할 때 단일 항목에서 지원하는 것보다 읽기 속도가 N배 높아지게 됩니다.

Bigtable에서 허용하는 것보다 높은 속도로 키 범위의 부분에 쓰기 작업을 수행해야 하는 경우에는 분할을 사용할 수 있습니다. 분할은 항목을 작은 부분으로 나눕니다. 이 원칙은 분할 카운터 문서에 설명되어 있습니다.

분할 시 발생할 수 있는 일반적인 실수에는 다음과 같은 것이 있습니다.

  • 시간 프리픽스를 사용한 분할. 시간이 다음 프리픽스로 넘어가면 분할되지 않은 새로운 부분이 핫스팟이 됩니다. 대신 쓰기 작업의 부분을 신규 프리픽스로 점차적으로 롤오버해야 합니다.

  • 가장 사용률이 높은 항목만 분할. 총 항목 중 소량의 일부만 분할하면 사용률이 높은 항목 간의 행이 충분하지 않아 서로 다른 분할에 상주하게 됩니다.

다음 단계

이 페이지가 도움이 되었나요? 평가를 부탁드립니다.

다음에 대한 의견 보내기...

Cloud Datastore 문서