Python 2.7 はサポートが終了しており、2026 年 1 月 31 日に
非推奨になります。非推奨になると、過去に組織のポリシーを使用して以前のランタイムのデプロイを再度有効にしていた場合でも、Python 2.7 アプリケーションをデプロイできなくなります。既存の Python 2.7 アプリケーションは、
非推奨日以降も引き続き実行され、トラフィックを受信します。
サポートされている最新バージョンの Python に移行することをおすすめします。
強整合性を実現するデータ構造設計
コレクションでコンテンツを整理
必要に応じて、コンテンツの保存と分類を行います。
注: 新しいアプリケーションを作成する際は、NDB クライアント ライブラリを使用することを強くおすすめします。NDB クライアント ライブラリには、Memcache API によるエンティティの自動キャッシュをはじめ、このクライアント ライブラリにはないメリットがあります。古い DB クライアント ライブラリを現在使用している場合は、DB から NDB への移行ガイドをお読みください。
Datastore は、多数のマシン間でデータを分散し、同期レプリケーションを広範な地理的領域で使用することで、高可用性、スケーラビリティ、耐久性を実現します。ただし、この設計では書き込みスループットが制限され、1 つのエンティティ グループに対する commit が 1 秒につき 1 回程度までとなります。また、複数のエンティティ グループにまたがるクエリやトランザクションに対しても制限があります。このページではこうした制限事項について詳しく説明します。また、アプリケーションの書き込みスループットに関する要件を満たしつつも、強整合性をサポートするようにデータを構築するためのおすすめの方法を紹介します。
強整合性読み取りは常に現在のデータを返します。トランザクション内で実行された場合は、単一の一貫性のあるスナップショットから得られたように見えます。ただし、クエリは強整合性を維持するために祖先フィルタを指定するか、トランザクションに参加する必要があります。トランザクションには最大で 25 のエンティティ グループを含めることができます。結果整合性読み取りにはこのような制限はなく、多くの場合に適しています。結果整合性読み取りを使用する場合は、大量のエンティティ グループ間でデータを分散できます。この結果、異なるエンティティ グループで commit を並列実行することで、書き込みスループットが向上します。ただし結果整合性読み取りがアプリケーションに適するかどうかを判断するには、その特性を理解しておく必要があります。
- 結果整合性読み取りによる結果は、最新のトランザクションを反映していない場合があります。このタイプの読み取りでは、読み取りを実行しているレプリカが最新であることが保証されないためです。代わりに結果整合性読み取りでは、クエリの実行時にこのレプリカで使用可能な任意のデータが使用されます。ほとんどの場合、レプリケーション レイテンシは数秒未満です。
- 複数のエンティティを対象として commit されたトランザクションは、すべてではなく一部のエンティティにしか適用されていないように見える場合があります。しかし、1 つのトランザクションが 1 つのエンティティ内で部分的に適用されたように見えることはありません。
- クエリ結果には、フィルタ条件に適合しないはずのエンティティが含まれる場合や、フィルタ条件に適合するはずのエンティティが除外される場合があります。これは、インデックスが読み取られるバージョンとエンティティ自体が読み取られるバージョンが異なる場合があるためです。
強整合性に対応するデータ構造化の方法を理解するため、単純なゲストブック アプリケーションを作成する 2 つの方法を比較してみます。最初の方法では、作成された各エンティティに対して新しいルート エンティティを作成します。
import webapp2
from google.appengine.ext import db
class Guestbook(webapp2.RequestHandler):
def post(self):
greeting = Greeting()
...
次に、エンティティの種類 Greeting
でクエリを実行し、直近 10 件のグリーティングを求めます。
import webapp2
from google.appengine.ext import db
class MainPage(webapp2.RequestHandler):
def get(self):
self.response.out.write('<html><body>')
greetings = db.GqlQuery("SELECT * "
"FROM Greeting "
"ORDER BY date DESC LIMIT 10")
しかし、非祖先クエリを使用しているため、このスキームでのクエリの実行に使用されているレプリカは、クエリが実行されるまで新しいグリーティングを見ていない可能性があります。それでも commit の数秒以内であれば、ほぼすべての書き込みが非祖先クエリで使用できます。多くのアプリケーションでは、現在のユーザー自身による変更を処理する場合、非祖先クエリによる結果を提供するソリューションで通常は十分であり、この程度のレプリケーション レイテンシは完全に許容範囲となります。
アプリケーションにとって強整合性が重要である場合、単一の強整合性祖先クエリで読み取る必要があるすべてのエンティティ間で同じルート エンティティを識別する祖先パスを使用してエンティティを書き込むという代替方法があります。
import webapp2
from google.appengine.ext import db
class Guestbook(webapp2.RequestHandler):
def post(self):
guestbook_name=self.request.get('guestbook_name')
greeting = Greeting(parent=guestbook_key(guestbook_name))
...
これで、共通のルート エンティティによって識別されるエンティティ グループ内で、強整合性祖先クエリを実行できます。
import webapp2
from google.appengine.ext import db
class MainPage(webapp2.RequestHandler):
def get(self):
self.response.out.write('<html><body>')
guestbook_name=self.request.get('guestbook_name')
greetings = db.GqlQuery("SELECT * "
"FROM Greeting "
"WHERE ANCESTOR IS :1 "
"ORDER BY date DESC LIMIT 10",
guestbook_key(guestbook_name))
この方法では、1 つのゲストブックにつき 1 つのエンティティ グループに書き込むことで、強整合性を実現できます。ただしゲストブックへの変更は、1 秒あたり 1 回のみの書き込みに制限されます(エンティティ グループに対してサポートされる制限)。アプリケーションの書き込み使用量が増えると予想される場合は、他の方法を検討する必要があります。たとえば、最新の投稿は有効期限付の Memcache に入れ、Memcache と Datastore から最新の投稿を組み合わせて表示する、Cookie に投稿をキャッシュする、URL など他のものに完全に状態を入れるなどです。現在のユーザーがアプリケーションに投稿している間に、このユーザーにデータを提供するキャッシュ ソリューションを見つけることが目標となります。トランザクション内で get、祖先クエリ、またはなんらかのオペレーションを実行する場合は、常に最新の書き込みデータが表示されることに留意してください。
特に記載のない限り、このページのコンテンツはクリエイティブ・コモンズの表示 4.0 ライセンスにより使用許諾されます。コードサンプルは Apache 2.0 ライセンスにより使用許諾されます。詳しくは、Google Developers サイトのポリシーをご覧ください。Java は 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\u003eDevelopers should utilize the NDB Client Library for new applications due to its advantages, such as automatic entity caching.\u003c/p\u003e\n"],["\u003cp\u003eDatastore offers high availability and scalability, but this comes with a limitation of about one commit per second for any single entity group.\u003c/p\u003e\n"],["\u003cp\u003eStrongly-consistent reads require an ancestor filter and provide current data within transactions, while eventually-consistent reads lack these constraints but might return outdated or incomplete data.\u003c/p\u003e\n"],["\u003cp\u003eFor applications requiring strong consistency, entities can be written with an ancestor path, but this method limits changes within a guestbook to one write per second.\u003c/p\u003e\n"],["\u003cp\u003eApplications encountering heavier write usage may consider caching strategies, such as using memcache or cookies, to manage recent posts and improve responsiveness for the current user.\u003c/p\u003e\n"]]],[],null,["# Structuring Data for Strong Consistency\n\n**Note:**\nDevelopers building new applications are **strongly encouraged** to use the\n[NDB Client Library](/appengine/docs/legacy/standard/python/ndb), which has several benefits\ncompared to this client library, such as automatic entity caching via the Memcache\nAPI. If you are currently using the older DB Client Library, read the\n[DB to NDB Migration Guide](/appengine/docs/legacy/standard/python/ndb/db_to_ndb)\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/python/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 import webapp2\n from google.appengine.ext import db\n\n class Guestbook(webapp2.RequestHandler):\n def post(self):\n greeting = Greeting()\n ...\n\nIt then queries on the entity kind `Greeting` for the ten most recent greetings. \n\n import webapp2\n from google.appengine.ext import db\n\n class MainPage(webapp2.RequestHandler):\n def get(self):\n self.response.out.write('\u003chtml\u003e\u003cbody\u003e')\n greetings = db.GqlQuery(\"SELECT * \"\n \"FROM Greeting \"\n \"ORDER BY date DESC 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 import webapp2\n from google.appengine.ext import db\n\n class Guestbook(webapp2.RequestHandler):\n def post(self):\n guestbook_name=self.request.get('guestbook_name')\n greeting = Greeting(parent=guestbook_key(guestbook_name))\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 import webapp2\n from google.appengine.ext import db\n\n class MainPage(webapp2.RequestHandler):\n def get(self):\n self.response.out.write('\u003chtml\u003e\u003cbody\u003e')\n guestbook_name=self.request.get('guestbook_name')\n\n greetings = db.GqlQuery(\"SELECT * \"\n \"FROM Greeting \"\n \"WHERE ANCESTOR IS :1 \"\n \"ORDER BY date DESC LIMIT 10\",\n guestbook_key(guestbook_name))\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/python/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."]]