Como estruturar dados com uma consistência forte
Mantenha tudo organizado com as coleções
Salve e categorize o conteúdo com base nas suas preferências.
O Datastore fornece alta disponibilidade, escalonabilidade e durabilidade ao
distribuir dados em muitas máquinas e usar a replicação
síncrona em uma ampla área geográfica. No entanto, a desvantagem desse
design é que a capacidade de gravação de qualquer
grupo de entidades está limitada a
uma confirmação por segundo, aproximadamente. Há também limitações relacionadas a consultas ou transações que
abrangem diversos grupos de entidades. Nesta página, mostramos essas limitações com detalhes e discutimos as práticas recomendadas para que os dados sejam compatíveis com uma consistência forte e ainda atendam aos requisitos de capacidade de gravação do aplicativo.
As leituras de consistência forte sempre retornam dados atuais e quando realizadas dentro de uma transação, surgem provenientes de um instantâneo único e consistente. No entanto, as consultas precisam especificar um filtro ancestral de consistência forte ou participar de uma transação e estas envolvem no máximo 25 grupos de entidades. As leituras de consistência eventual não têm essas limitações e são adequadas em muitos casos. O uso de leituras de consistência eventual permite a distribuição dos dados entre um número maior de grupos de entidades e faz com que você receba maior capacidade de gravação, executando commits em paralelo nos diferentes grupos de entidades. No entanto, é preciso entender as características das leituras de consistência eventual para determinar se elas são adequadas ao aplicativo:
- Os resultados dessas leituras talvez não reflitam as transações mais recentes. Isso pode ocorrer porque essas leituras não garantem que a réplica na qual elas estão sendo executadas está atualizada. Em vez disso, elas usam os dados disponíveis na réplica no momento da execução da consulta. A latência da replicação é quase sempre inferior a alguns segundos.
- Uma transação commit que abrange diversas entidades talvez tenha sido aplicada em algumas entidades e não em outras. Observe, no entanto, que uma transação nunca parecerá ter sido parcialmente aplicada em uma única entidade.
- Os resultados da consulta incluem entidades que não atendem aos critérios de filtro e excluem entidades que atendem a esses mesmos critérios. Isso ocorre porque os índices podem ser lidos em uma versão diferente da que a própria entidade é lida.
Para compreender como estruturar os dados para consistência forte, compare duas abordagens diferentes de um aplicativo simples de livro de visitas. A primeira abordagem
cria uma nova entidade raiz para cada entidade criada:
Em seguida, consulta o tipo de entidade Greeting
para as dez saudações mais recentes.
No entanto, como você usa uma consulta que não é de ancestral, a réplica usada para executá-la
nesse esquema talvez não tenha visto a nova saudação no momento
da execução. No entanto, quase todas as gravações estarão disponíveis para consultas não ancestrais dentro de alguns segundos do commit. Para muitos aplicativos, normalmente uma solução que fornece os resultados de uma consulta não ancestral no contexto das próprias alterações do usuário atual será suficiente para tornar essas latências de replicação completamente aceitáveis.
Caso a consistência forte seja importante para o aplicativo, uma abordagem alternativa é gravar entidades com um caminho ancestral que identifique a mesma entidade raiz em todas as entidades a serem lidas em uma única consulta de ancestral de consistência forte:
Será possível executar uma consulta de ancestral de consistência forte no grupo de entidades identificado pela entidade raiz comum:
Esta abordagem atinge uma consistência forte na gravação para um único grupo de entidades por livro de visitas, mas também limita as alterações nele para não mais do que 1 gravação por segundo, limite suportado para grupos de entidades. Se o aplicativo tiver maior
probabilidade de uso de gravação, talvez seja necessário usar
outros meios: por exemplo, é possível colocar postagens recentes em um
memcache com uma expiração
e exibir uma combinação de postagens recentes do memcache e do
Datastore, ou então armazená-los em um cookie, colocar um pouco de estado
no URL ou algo totalmente diferente. O objetivo é encontrar uma solução de
armazenamento em cache que forneça os dados ao usuário atual no período de tempo em que o
usuário estiver postando no aplicativo. Se executar o comando "get", uma consulta
de ancestral ou qualquer operação em uma transação, você sempre verá os dados
gravados mais recentemente.
Exceto em caso de indicação contrária, o conteúdo desta página é licenciado de acordo com a Licença de atribuição 4.0 do Creative Commons, e as amostras de código são licenciadas de acordo com a Licença Apache 2.0. Para mais detalhes, consulte as políticas do site do Google Developers. Java é uma marca registrada da Oracle e/ou afiliadas.
Última atualização 2025-09-04 UTC.
[[["Fácil de entender","easyToUnderstand","thumb-up"],["Meu problema foi resolvido","solvedMyProblem","thumb-up"],["Outro","otherUp","thumb-up"]],[["Difícil de entender","hardToUnderstand","thumb-down"],["Informações incorretas ou exemplo de código","incorrectInformationOrSampleCode","thumb-down"],["Não contém as informações/amostras de que eu preciso","missingTheInformationSamplesINeed","thumb-down"],["Problema na tradução","translationIssue","thumb-down"],["Outro","otherDown","thumb-down"]],["Última atualização 2025-09-04 UTC."],[[["\u003cp\u003eThis API is compatible with first-generation runtimes and can be used when upgrading to the corresponding second-generation runtimes, with a separate migration guide available for those updating to App Engine Java 11/17.\u003c/p\u003e\n"],["\u003cp\u003eDatastore ensures high availability, scalability, and durability through data distribution and synchronous replication, but write throughput is limited to one commit per second for a single entity group, with restrictions on queries or transactions spanning multiple groups.\u003c/p\u003e\n"],["\u003cp\u003eStrongly-consistent reads provide current data and a consistent snapshot within transactions, requiring an ancestor filter for queries and limiting transactions to 25 entity groups, while eventually-consistent reads offer more flexibility but may not reflect the latest transactions.\u003c/p\u003e\n"],["\u003cp\u003eFor applications needing strong consistency, structuring data with a common ancestor path enables strongly-consistent ancestor queries, although this limits writes to one per second per entity group.\u003c/p\u003e\n"],["\u003cp\u003eFor applications with high write usage, consider using alternative solutions such as memcache or cookies to cache recent posts and display a mix of recent data alongside Datastore results.\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| java-gen2\n|\n| /services/access). If you are updating to the App Engine Java 11/17 runtime, refer to the [migration guide](/appengine/migration-center/standard/migrate-to-second-gen/java-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/java/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 protected Entity createGreeting(\n DatastoreService datastore, User user, Date date, String content) {\n // No parent key specified, so Greeting is a root entity.\n Entity greeting = new Entity(\"Greeting\");\n greeting.setProperty(\"user\", user);\n greeting.setProperty(\"date\", date);\n greeting.setProperty(\"content\", content);\n\n datastore.put(greeting);\n return greeting;\n }\n\nIt then queries on the entity kind `Greeting` for the ten most recent greetings. \n\n protected List\u003cEntity\u003e listGreetingEntities(DatastoreService datastore) {\n Query query = new Query(\"Greeting\").addSort(\"date\", Query.SortDirection.DESCENDING);\n return datastore.prepare(query).asList(FetchOptions.Builder.withLimit(10));\n }\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 protected Entity createGreeting(\n DatastoreService datastore, User user, Date date, String content) {\n // String guestbookName = \"my guestbook\"; -- Set elsewhere (injected to the constructor).\n Key guestbookKey = KeyFactory.createKey(\"Guestbook\", guestbookName);\n\n // Place greeting in the same entity group as guestbook.\n Entity greeting = new Entity(\"Greeting\", guestbookKey);\n greeting.setProperty(\"user\", user);\n greeting.setProperty(\"date\", date);\n greeting.setProperty(\"content\", content);\n\n datastore.put(greeting);\n return greeting;\n }\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 protected List\u003cEntity\u003e listGreetingEntities(DatastoreService datastore) {\n Key guestbookKey = KeyFactory.createKey(\"Guestbook\", guestbookName);\n Query query =\n new Query(\"Greeting\", guestbookKey)\n .setAncestor(guestbookKey)\n .addSort(\"date\", Query.SortDirection.DESCENDING);\n return datastore.prepare(query).asList(FetchOptions.Builder.withLimit(10));\n }\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/java/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."]]