Go 1.11은 지원이 종료되었으며 2026년 1월 31일에
지원 중단됩니다. 지원 중단 후에는 조직에서 이전에 조직 정책을 사용하여 레거시 런타임의 배포를 다시 사용 설정한 경우에도 Go 1.11 애플리케이션을 배포할 수 없습니다. 기존 Go 1.11 애플리케이션은
지원 중단 날짜 이후에도 계속 실행되고 트래픽을 수신합니다.
지원되는 최신 Go 버전으로 마이그레이션하는 것이 좋습니다.
강력한 일관성을 위한 데이터 구조화
컬렉션을 사용해 정리하기
내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요.
Datastore는 여러 머신에 데이터를 배포하고 광범위한 지리적 영역에 걸쳐 동기 복제를 사용하는 방법으로 고가용성, 확장성, 내구성을 제공합니다. 하지만 이러한 설계에서는 단일 항목 그룹의 쓰기 처리량이 초당 커밋 약 1회로 제한되고 여러 항목 그룹에 걸쳐 있는 쿼리나 트랜잭션에 제한이 있다는 단점이 있습니다. 이 페이지에서는 이러한 제한 사항을 자세히 설명하고 애플리케이션의 쓰기 처리량 요구사항을 충족하면서도 Strong Consistency를 지원하도록 데이터를 구조화하기 위한 권장사항을 설명합니다.
강력한 일관성을 가진 읽기는 항상 최신 데이터를 반환하며, 트랜잭션 내에서 수행되면 일관된 단일 스냅샷에서 가져온 것처럼 보입니다. 하지만 강력한 일관성을 갖거나 트랜잭션에 참여하려면 쿼리가 상위 필터를 지정해야 하며, 트랜잭션에는 항목 그룹이 최대 25개까지 포함될 수 있습니다. 최종 일관성을 가진 읽기는 이러한 제한에 적용되지 않고, 대부분의 경우 그 자체로 충분합니다. 최종 일관성을 가진 읽기를 사용하면 많은 수의 항목 그룹들에 데이터를 배포할 수 있으므로, 여러 항목 그룹에서 커밋을 동시에 실행하여 쓰기 처리량을 더 높일 수 있습니다. 하지만 애플리케이션에 적합한지 여부를 결정하려면 최종 일관성을 가진 읽기의 특성을 이해해야 합니다.
- 이러한 읽기의 결과는 최신 트랜잭션을 반영하지 않을 수도 있습니다. 이러한 경우가 발생하는 이유는 읽기에서 실행 중인 복제본이 최신 상태인지 확인하지 않기 때문입니다. 그 대신 쿼리 실행 시점에 해당 복제본에 사용 가능한 데이터를 무조건 사용합니다. 복제 지연 시간은 거의 항상 몇 초 미만입니다.
- 여러 항목에 걸쳐 있는 커밋된 트랜잭션은 일부 항목에만 적용된 것처럼 보일 수 있습니다. 하지만 단일 항목 내에서 하나의 트랜잭션이 부분적으로 적용된 것처럼 보이는 일은 절대 없습니다.
- 쿼리 결과에 필터 기준에 따라 포함되지 않아야 하는 항목이 포함되고 포함되어야 하는 항목이 제외될 수도 있습니다. 이러한 경우가 발생하는 이유는 항목 자체를 읽을 때의 버전과 다른 버전에서 색인을 읽을 수 있기 때문입니다.
Strong Consistency를 위해 데이터를 구조화하는 방법을 이해하기 위해 단순한 방명록 애플리케이션에 두 가지 다른 접근 방식을 적용한 결과를 비교해 보겠습니다. 첫 번째 방식은 생성된 각 항목에 새로운 루트 항목을 만듭니다.
그런 다음 최근 10개 인사말의 Greeting
항목 종류를 쿼리합니다.
하지만 비상위 쿼리를 사용하고 있으므로 이 스키마에서 쿼리를 수행하는 데 사용된 복제본이 쿼리 실행 시점에 새 인사말을 보지 못할 수 있습니다. 그럼에도 불구하고 커밋한 후 몇 초 이내에 비상위 쿼리에 거의 모든 쓰기를 사용할 수 있습니다. 많은 애플리케이션의 경우 현재 사용자의 자체 변경사항이라는 컨텍스트에서 비상위 쿼리의 결과를 제공하는 솔루션을 활용하는 것만으로도 일반적으로 이러한 복제 지연 시간을 완전히 허용 가능한 수준으로 유지하는 데 충분합니다.
강력한 일관성이 애플리케이션에 중요한 경우, 다른 접근 방식은 강력한 일관성을 가진 단일 상위 쿼리에서 읽어야 하는 모든 항목에 동일한 루트 항목을 식별하는 상위 경로로 항목을 쓰는 것입니다.
그러면 공통된 루트 항목으로 식별된 항목 그룹 내에서 강력한 일관성을 가진 상위 쿼리를 수행할 수 있습니다.
이 방식은 방명록별로 단일 항목 그룹에 쓰기를 수행하여 strong consistency를 달성하지만 방명록에 대한 변경이 초당 쓰기 1회 이하로 제한됩니다(항목 그룹에 지원되는 제한). 애플리케이션에 쓰기 작업이 많이 수행될 가능성이 높은 경우에는 다른 방법을 사용하는 것이 좋을 수도 있습니다. 예를 들어 Memcache에 만료 시간이 있는 최신 게시물을 넣고 Memcache와 Datastore의 최신 게시물을 섞어서 표시하거나 쿠키로 최근 게시물을 캐시하고 URL에 상태를 설정하거나 또는 전혀 다른 방법을 사용할 수도 있습니다. 목표는 사용자가 애플리케이션에 게시물을 게시하는 기간 동안 현재 사용자에게 데이터를 제공하는 캐싱 솔루션을 찾는 것입니다. get, 상위 쿼리, 트랜잭션 내 작업을 수행할 때는 항상 가장 최근에 작성된 데이터가 표시됩니다.
달리 명시되지 않는 한 이 페이지의 콘텐츠에는 Creative Commons Attribution 4.0 라이선스에 따라 라이선스가 부여되며, 코드 샘플에는 Apache 2.0 라이선스에 따라 라이선스가 부여됩니다. 자세한 내용은 Google Developers 사이트 정책을 참조하세요. 자바는 Oracle 및/또는 Oracle 계열사의 등록 상표입니다.
최종 업데이트: 2025-09-04(UTC)
[[["이해하기 쉬움","easyToUnderstand","thumb-up"],["문제가 해결됨","solvedMyProblem","thumb-up"],["기타","otherUp","thumb-up"]],[["이해하기 어려움","hardToUnderstand","thumb-down"],["잘못된 정보 또는 샘플 코드","incorrectInformationOrSampleCode","thumb-down"],["필요한 정보/샘플이 없음","missingTheInformationSamplesINeed","thumb-down"],["번역 문제","translationIssue","thumb-down"],["기타","otherDown","thumb-down"]],["최종 업데이트: 2025-09-04(UTC)"],[[["\u003cp\u003eThis API is designed for first-generation runtimes and is relevant when upgrading to corresponding second-generation runtimes, with a migration guide provided for those updating to App Engine Go 1.12+.\u003c/p\u003e\n"],["\u003cp\u003eDatastore offers high availability, scalability, and durability through data distribution and synchronous replication, but this design limits write throughput to approximately one commit per second for any single entity group.\u003c/p\u003e\n"],["\u003cp\u003eStrongly-consistent reads guarantee current data but require an ancestor filter for queries and are limited to 25 entity groups per transaction, whereas eventually-consistent reads do not have these restrictions but may not reflect the latest transactions.\u003c/p\u003e\n"],["\u003cp\u003eStructuring data with a common root entity across all entities ensures strong consistency through ancestor queries, although it caps guestbook changes to one write per second, potentially necessitating other caching methods for higher write usage.\u003c/p\u003e\n"],["\u003cp\u003eNon-ancestor queries do not guarantee the latest data immediately, but nearly all writes are typically available within a few seconds, making them suitable for applications where slight replication delays are acceptable.\u003c/p\u003e\n"]]],[],null,["# Structuring Data for Strong Consistency\n\n| This API is supported for first-generation runtimes and can be used when [upgrading to corresponding second-generation runtimes](/appengine/docs/standard/\n| go\n| /services/access). If you are updating to the App Engine Go 1.12+ runtime, refer to the [migration guide](/appengine/migration-center/standard/migrate-to-second-gen/go-differences) to learn about your migration options for legacy bundled services.\n\nDatastore provides high availability, scalability and durability by\ndistributing data over many machines and using synchronous\nreplication over a wide geographic area. However, there is a tradeoff in this\ndesign, which is that the write throughput for any single\n[*entity group*](/appengine/docs/legacy/standard/go111/datastore/entities#Ancestor_paths) is limited to about\none commit per second, and there are limitations on queries or transactions that\nspan multiple entity groups. This page describes these limitations in more\ndetail and discusses best practices for structuring your data to support strong\nconsistency while still meeting your application's write throughput\nrequirements.\n\nStrongly-consistent reads always return current data, and, if performed within a\ntransaction, will appear to come from a single, consistent snapshot. However,\nqueries must specify an ancestor filter in order to be strongly-consistent or\nparticipate in a transaction, and transactions can involve at most 25 entity\ngroups. Eventually-consistent reads do not have those limitations, and are\nadequate in many cases. Using eventually-consistent reads can allow you to\ndistribute your data among a larger number of entity groups, enabling you to\nobtain greater write throughput by executing commits in parallel on the\ndifferent entity groups. But, you need to understand the characteristics of\neventually-consistent reads in order to determine whether they are suitable for\nyour application:\n\n- The results from these reads might not reflect the latest transactions. This can occur because these reads do not ensure that the replica they are running on is up-to-date. Instead, they use whatever data is available on that replica at the time of query execution. Replication latency is almost always less than a few seconds.\n- A committed transaction that spanned multiple entities might appear to have been applied to some of the entities and not others. Note, though, that a transaction will never appear to have been partially applied within a single entity.\n- The query results can include entities that should not have been included according to the filter criteria, and might exclude entities that should have been included. This can occur because indexes might be read at a different version than the entity itself is read at.\n\nTo understand how to structure your data for strong consistency, compare two\ndifferent approaches for a simple guestbook application. The first approach\ncreates a new root entity for each entity that is created: \n\n g := Greeting{ /* ... */ }\n key := datastore.NewIncompleteKey(ctx, \"Greeting\", nil)\n\nIt then queries on the entity kind `Greeting` for the ten most recent greetings. \n\n q := datastore.NewQuery(\"Greeting\").Order(\"-Date\").Limit(10)\n\nHowever, because you are using a non-ancestor query, the replica used to perform\nthe query in this scheme might not have seen the new greeting by the time the\nquery is executed. Nonetheless, nearly all writes will be available for\nnon-ancestor queries within a few seconds of commit. For many applications, a\nsolution that provides the results of a non-ancestor query in the context of the\ncurrent user's own changes will usually be sufficient to make such replication\nlatencies completely acceptable.\n\nIf strong consistency is important to your application, an alternate approach is\nto write entities with an ancestor path that identifies the same root entity\nacross all entities that must be read in a single, strongly-consistent ancestor\nquery: \n\n g := Greeting{ /* ... */ }\n key := datastore.NewIncompleteKey(ctx, \"Greeting\", guestbookKey(ctx))\n\nYou will then be able to perform a strongly-consistent ancestor query within the\nentity group identified by the common root entity: \n\n q := datastore.NewQuery(\"Greeting\").Ancestor(guestbookKey(ctx)).Order(\"-Date\").Limit(10)\n\nThis approach achieves strong consistency by writing to a single entity group\nper guestbook, but it also limits changes to the guestbook to no more than\n1 write per second (the supported limit for entity groups). If your application\nis likely to encounter heavier write usage, you might need to consider using\nother means: for example, you might put recent posts in a\n[memcache](/appengine/docs/legacy/standard/go111/memcache) with an expiration\nand display a mix of recent posts from the memcache and\nDatastore, or you might cache them in a cookie, put some state\nin the URL, or something else entirely. The goal is to find a caching solution\nthat provides the data for the current user for the period of time in which the\nuser is posting to your application. Remember, if you do a get, an ancestor\nquery, or any operation within a transaction, you will always see the most\nrecently written data."]]