Cloud Datastore 用の Python DB クライアント ライブラリ

注: 新しいアプリケーションを作成する際には NDB クライアント ライブラリを使用することを強くおすすめします。NDB クライアント ライブラリには、Memcache API によるエンティティの自動キャッシュなど、DB クライアント ライブラリにはないメリットがあります。古い DB クライアント ライブラリを使用している場合は、DB から NDB への移行ガイドをお読みください。

このドキュメントでは、Cloud Datastore に格納されるオブジェクトのデータモデル、API を使用したクエリの作成方法、トランザクションの処理方法について説明します。

エンティティ

Cloud Datastore のオブジェクトをエンティティと呼びます。エンティティには 1 つ以上の名前付きプロパティがあり、各プロパティには 1 つ以上の値を設定できます。プロパティ値には、整数、浮動小数点数、文字列、日付、バイナリデータなど、さまざまなデータ型があります。複数の値を持つプロパティに対してクエリを実行すると、いずれかの値がクエリの条件を満たしているかどうかが評価されます。このため、メンバーかどうかを評価をするときに、そのようなプロパティが役立ちます。

種類、キー、識別子

Cloud Datastore の各エンティティは特定の種類に属しています。種類とは、クエリ用にエンティティを分類するカテゴリのことです。たとえば、人事アプリケーションでは、会社の各従業員を Employee という種類のエンティティで表します。また、各エンティティに独自のキーがあり、これによりエンティティが一意に識別されます。キーは次のコンポーネントで構成されています。

  • エンティティの種類
  • 識別子。次のいずれかになります。
    • キー名の文字列
    • 整数の ID
  • オプションの祖先パス。Cloud Datastore 階層内でのエンティティの位置を指定します。

識別子は、エンティティの作成時に割り当てられます。識別子はエンティティのキーの一部であるため、エンティティに永続的に関連付けられており、変更できません。識別子を割り当てる方法は 2 通りあります。

  • アプリケーションでエンティティの固有のキー名文字列を指定する。
  • エンティティに整数の数値 ID が自動的に割り当てられるように Cloud Datastore を設定する。

祖先パス

Cloud Datastore 内の各エンティティは、ファイル システムのディレクトリ構造と同様の階層的に構造化された空間を形成します。エンティティを作成するときに、別のエンティティを親として設定することもできます。新しいエンティティは、この親エンティティの子になります(ファイル システムとは異なり、親エンティティが実際に存在している必要はありません)。親を持たないエンティティは、ルート エンティティとなります。 エンティティと親との割り当ては永続的であり、エンティティの作成後は変更できません。同じ親を持つ 2 つのエンティティ、または 2 つのルート エンティティ(親を持たないエンティティ)に同じ数値 ID が割り当てられることはありません。

エンティティの親、親の親、さらにその親などは順に祖先として位置付けられ、エンティティの子、子の子、さらにその子などは順に子孫として位置付けられます。 ルート エンティティとその子孫のエンティティはすべて同じエンティティ グループに属します。 ルート エンティティから始まり、親から子を経由して対象のエンティティに至るまでの連なりをエンティティの祖先パスといいます。 エンティティを識別する完全なキーは、エンティティの祖先パスから始まってそのエンティティ自身で終わる一連の「種類と識別子のペア」で構成されます。

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

ルート エンティティの場合は祖先パスが空で、エンティティ自身の種類と識別子だけでキーが構成されています。

[Person:GreatGrandpa]

この概念を次の図に示します。

エンティティ グループ

クエリとインデックス

アプリケーションは、キーにより Cloud Datastore から直接エンティティを取得できるだけでなく、クエリを実行してエンティティのプロパティ値によりエンティティを取得することもできます。クエリは特定の種類のエンティティに対して動作します。クエリでは、エンティティのプロパティ値、キー、および祖先に対してフィルタを指定でき、0 個以上のエンティティを結果として返すことができます。 クエリでは並べ替え順を指定し、プロパティ値を基準として結果を並べることもできます。結果に含まれるエンティティは、フィルタと並べ替え順で指定されたすべてのプロパティに値が 1 つ以上存在し(null も可)、プロパティ値がすべてのフィルタ条件を満たしたものすべてです。クエリは、エンティティ全体、射影されたエンティティ、またはエンティティのキーだけを返すこともできます。

一般的に、クエリは次のような要素で構成されます。

クエリを実行すると、指定したすべてのフィルタ条件を満たす、指定した種類のすべてのエンティティが取得され、指定した順序で並べ替えられます。クエリは読み取り専用として実行されます。

注: メモリを節約し、パフォーマンスを向上させるため、クエリでは可能な限り返される結果の数に対する制限を指定してください。

クエリに祖先フィルタを設定すると、指定した祖先の下位にあるエンティティ グループだけが返されるように結果を制限できます。このようなクエリを祖先クエリといいます。デフォルトでは、祖先クエリは強整合性を持つ結果、つまりデータに対する最新の変更内容が反映された最新の結果を返します。祖先クエリ以外のクエリでは、1 つのエンティティ グループではなく Cloud Datastore 全体に対象が広がり、結果整合性しか持たないため、最新でない結果が返される可能性があります。強整合性が必要なアプリケーションでデータ構造を定義する場合には、祖先を指定してクエリで取得できるように、関連するエンティティを同じエンティティ グループに配置することも検討してください。詳細については、強整合性に対応するデータ構造を参照してください。

App Engine は、エンティティの各プロパティに単純なインデックスを事前に定義します。さらに、App Engine アプリケーションでは独自のインデックスを index.yaml という名前のインデックス構成ファイルに定義することもできます。開発用サーバーは、既存のインデックスでは実行できないクエリが出現したときに、このファイルに自動的に候補を追加します。アプリケーションをアップロードする前にこのファイルを編集して、インデックスを手動で調整できます。

注: インデックス ベースのクエリ メカニズムはさまざまなクエリをサポートしているため、ほとんどのアプリケーションに適しています。ただし、他のデータベース テクノロジーでは一般にサポートされているいくつかの種類のクエリがサポートされていません。特に Cloud Datastore のクエリエンジンでは、結合と集計クエリがサポートされていない点に注意してください。Cloud Datastore クエリの制限については、データストアのクエリのページをご覧ください。

トランザクション

エンティティの挿入、更新、削除はすべてトランザクションのコンテキスト内で試行されます。1 つのトランザクションに、このようなオペレーションをいくつでも含めることができます。すべてのオペレーションはトランザクションごとに 1 つの単位として Cloud Datastore に適用されるので、データの整合性が維持されます。いずれかのオペレーションが失敗した場合、どのオペレーションも適用されません。

1 つのエンティティに対する複数のオペレーションを 1 つのトランザクションで実行できます。たとえば、オブジェクトのカウンタ フィールドの値を増やす場合、カウンタの値を読み取り、新しい値を計算してフィールドに保存する必要があります。トランザクションを使用しないでこのオペレーションを行うと、値の読み取り時点から更新時点までの間にカウンタの値が別のプロセスによって更新されてしまうことがあり、アプリケーションがその更新された値を上書きすることになる可能性があります。1 つのトランザクションで読み取り、計算、書き込みを行うことで、他のプロセスによる影響を防ぐことができます。

トランザクションとエンティティ グループ

トランザクションで使用できるクエリは祖先クエリだけです。つまり、トランザクションで実行するクエリの対象は単一のエンティティ グループに限定されます。トランザクション自体は複数のエンティティを処理できます。単一のエンティティ グループに属するものだけでなく、最大 25 の異なるエンティティ グループ(クロスグループ トランザクションの場合)を処理できます。

Cloud Datastore は、楽観的同時実行を使用してトランザクションを管理します。2 つ以上のトランザクションが、既存エンティティの更新や新しいエンティティの作成といった変更を同じエンティティ グループに対して同時に実行しようとすると、最初に commit したトランザクションが成功し、残りのトランザクションは commit に失敗します。とはいえ、失敗した残りのトランザクションを使って、更新されたデータに対してオペレーションを再試行できます。このような特長があるため、特定のエンティティ グループのエンティティに対して同時に可能な書き込み数は制限されます。

クロスグループ トランザクション

異なるエンティティ グループに属する複数のエンティティに対するトランザクションをクロスグループ(XG)トランザクションといいます。1 つのトランザクションは、最大 25 のエンティティ グループに適用できます。あるトランザクションの適用先となるいずれかのエンティティ グループが他の同時実行トランザクションによって変更される場合を除き、そのトランザクションの処理は成功します。アトミックな書き込みを行うためだけの理由で同じ祖先の下位に異なる種類のデータを無理に配置する必要がないため、データを柔軟に編成することができます。

単一グループ トランザクションと同様に、XG トランザクションでは祖先クエリ以外のクエリを実行することはできません。とはいえ、複数の祖先クエリがある場合、各クエリを別個のエンティティ グループに対して実行できます。非トランザクション クエリ(祖先クエリ以外のクエリ)では、その前に commit されたトランザクションの結果のすべてまたは一部を取得できる場合もありますし、できない場合もあります(この問題の背景については、Cloud Datastore への書き込みとデータの可視性をご覧ください)。ただし、そのような非トランザクション クエリでは、部分的に commit された単一グループ トランザクションの結果より、部分的に commit された XG トランザクションの結果を返す可能性が高くなります。

1 つのエンティティ グループしか処理しない XG トランザクションのパフォーマンスとコストは、XG 以外の単一グループ トランザクションとまったく同じになります。複数のエンティティ グループを処理する XG トランザクションの場合、オペレーションのコストは XG 以外のトランザクションで実行した場合と同じになりますが、レイテンシが長くなる可能性があります。

Cloud Datastore の書き込みとデータの可視性

データは、以下の 2 つのフェーズで Cloud Datastore に書き込まれます。

  1. commit フェーズ。エンティティ データは大半のレプリカのトランザクション ログに記録されます。エンティティ データが記録されなかったレプリカは、最新のログがないことを示すマークが付きます。
  2. 適用フェーズ。レプリカごとに個別に発生します。このフェーズでは、次の 2 つのアクションが並列処理されます。
    • エンティティ データがそのレプリカに書き込まれます。
    • エンティティのインデックス行がそのレプリカに書き込まれます(この処理は、データ自体の書き込みより時間がかかる場合があります)。

書き込みオペレーションは commit フェーズの直後に戻り、適用フェーズが非同期で実行されます。このフェーズは、レプリカごとに異なる時間に実行される可能性が高いため、commit フェーズの完了から数百ミリ秒ほどの遅延が発生する場合があります。commit フェーズでエラーが発生すると、自動的に再試行されますが、エラーが繰り返し発生すると、Cloud Datastore がアプリケーション例外を通知するエラー メッセージを返します。commit フェーズが完了した後、特定のレプリカで適用フェーズが失敗した場合、以下のいずれかが発生すると、そのレプリカで適用が完了までロールフォワードされします。

  • Cloud Datastore の定期的なスイープで未完了の commit ジョブの有無が検査され、それらが適用される。
  • 影響を受けるエンティティ グループを使用する特定のオペレーション(getputdelete、祖先クエリ)が行われたために、次のオペレーションに進む前に、commit 後に未適用の変更が、オペレーションを実行中のレプリカで完了する。

この書き込み動作は、commit フェーズと適用フェーズのそれぞれで、アプリケーションでデータの取得がどのように可能になるかとそのタイミングに影響を及ぼす可能性があります。

  • 書き込みオペレーションでタイムアウト エラーが発生した場合、データの読み取りを行わないと、オペレーションが成功したのか失敗したのか判断できません。
  • Cloud Datastore の get と祖先クエリを実行すると、実行対象となるレプリカに対して未処理のすべての変更が適用されるため、それまでに成功したすべてのトランザクションは一貫した状態で取得できるようになります。つまり、get オペレーション(更新されたエンティティをキーで検索)を実行すると、そのエンティティの最新バージョンを取得できます。
  • 祖先クエリ以外のクエリは、最新のトランザクションが適用されていないレプリカで実行される可能性があるため、古い結果が返される場合があります。たとえ未処理のトランザクションを必ず適用するようなオペレーションを実行したとしても、そのようになる可能性があります。クエリが前のオペレーションとは異なるレプリカで実行される場合があるためです。
  • 同時変更のタイミングが祖先クエリ以外のクエリの結果に影響を及ぼす可能性があります。クエリ条件を満たしていたエンティティが変更され、条件を満たさなくなった場合、クエリを実行したレプリカのインデックスに変更がまだ適用されていないと、エンティティがクエリの結果セットに残ってしまう可能性があります。=

Cloud Datastore の統計情報

Cloud Datastore は、指定の種類のエンティティの数や所定のタイプのプロパティ値に使用されている領域の容量など、アプリケーションのために保存されているデータの統計情報を維持します。このような統計情報は、GCP Console の Cloud Datastore ダッシュボード ページで確認できます。また、Cloud Datastore API を使用すると、アプリケーション内からブログラムで具体的に名前を指定してエンティティを検索し、これらの値にアクセスできます。詳細については、Python のデータストア統計をご覧ください。

Python データストアの例

Python API では、モデルでエンティティの種類を記述します。その種類には、エンティティのプロパティの構成や種類などが含まれます。アプリケーションでは、Python のクラスを使用してモデルを定義し、クラス属性でプロパティを記述します。クラスの名前は、エンティティの種類の名前になります。特定の種類のエンティティは、モデルクラスのインスタンスによって表され、インスタンス属性はプロパティ値を表します。新しいエンティティを作成するには、目的のクラスのオブジェクトを作成し、その属性を設定して保存します(以下のように put() のようなメソッドを呼び出します)。

import datetime
from google.appengine.ext import db
from google.appengine.api import users

class Employee(db.Model):
  name = db.StringProperty(required=True)
  role = db.StringProperty(required=True,
                           choices=set(["executive", "manager", "producer"]))
  hire_date = db.DateProperty()
  new_hire_training_completed = db.BooleanProperty(indexed=False)
  email = db.StringProperty()

e = Employee(name="John",
             role="manager",
             email=users.get_current_user().email())
e.hire_date = datetime.datetime.now().date()
e.put()

Cloud Datastore API ではクエリ用インターフェースとして、クエリ オブジェクト インターフェースと、SQL に似た GQL というクエリ言語の 2 つが用意されています。クエリはモデルクラスのインスタンスの形式でエンティティを返します。

training_registration_list = ["Alfred.Smith@example.com",
                              "jharrison@example.com",
                              "budnelson@example.com"]
employees_trained = db.GqlQuery("SELECT * FROM Employee WHERE email IN :1",
                                training_registration_list)
for e in employees_trained:
  e.new_hire_training_completed = True
  db.put(e)
このページは役立ちましたか?評価をお願いいたします。

フィードバックを送信...

Python の App Engine スタンダード環境