ここに?げたベスト プラクティスは、Google Cloud Datastore を使用するアプリケ?ションを構築するときに留意すべきことをすばやく?照するために役立ちます。このペ?ジは Google Cloud Datastore の基本的な使い方を?明するものではないため、Google Cloud Datastore を使い始める際の出?点としてはおすすめしません。新しいユ?ザ?の方は
Google Cloud Datastore API スタ?トガイド
をご?ください。
全般
- 名前空間名、種類名、プロパティ名、カスタムキ?名には常に UTF-8 文字を使用します。これらの名前で非 UTF-8 文字を使用すると、Cloud Datastore の機能に影響する場合があります。たとえばプロパティ名で非 UTF-8 文字を使用すると、そのプロパティを使用するインデックスを作成できない場合があります。
- 種類名またはカスタムキ?名でスラッシュ(
/
)を使用しないでください。これらの名前でスラッシュを使用すると、??リリ?スされる機能に影響する可能性があります。
- Cloud のプロジェクト ID には機密情報を含めないでください。Cloud のプロジェクト ID は、プロジェクトの有?期間を超えて存?する場合があります。
API 呼び出し
- ?み取り、書き?み、削除には、?一のオペレ?ションではなく
バッチ オペレ?ション
を使用します。バッチ オペレ?ションでは、?一オペレ?ションと同じオ?バ?ヘッドで複?のオペレ?ションが?行されるため?率的です。
- トランザクション
が失敗した場合はロ?ルバックを試行してください。ロ?ルバックを使用すると、トランザクション?で同じリソ?スを使用する別のリクエストの再試行レイテンシが最小限に抑えられます。ロ?ルバック自?が失敗する可能性もあるため、ロ?ルバックはベスト エフォ?ト型の試行である点に注意してください。
- 可能な場合は、同期呼び出しではなく非同期呼び出しを使用します。非同期呼び出しでは、レイテンシの影響が最小限に抑えられます。たとえば、同期
lookup()
の結果とクエリの結果に?ってレスポンスをレンダリングするアプリケ?ションについて考えてみましょう。
lookup()
とクエリにデ?タの依存?係がない場合、
lookup()
が完了するまでクエリの開始を同期的に待機する必要はありません。
エンティティ
- ?連性の高いデ?タは
エンティティ グル?プ
にグル?プ化します。エンティティ グル?プを使用すると
祖先クエリ
が可能となり、?整合性のある結果が得られます。エンティティ グル?プ?のエンティティは Cloud Datastore サ?バ?上の物理的に近い場所に保存されるため、祖先クエリでは I/O を最小限に抑えて迅速にエンティティ グル?プをスキャンすることもできます。
- エンティティ グル?プへの書き?みは 1 秒間に 1 回を超えないようにします。この制限を超える速度で書き?みを?けると、結果整合性の?み取りはさらに結果的になり、?整合性の?み取りはタイムアウトにつながります。そのためアプリケ?ションの全?的なパフォ?マンスが低下します。エンティティ グル?プに?するバッチ?理またはトランザクション?理による書き?みは、この制限に?して 1 回の書き?みとしてカウントされます。
- 1 回の commit に同じエンティティ(キ?が同一のエンティティ)を複?回含めないでください。1 回の commit に同じエンティティを複?回含めると、Cloud Datastore のレイテンシに影響する場合があります。
キ?
- エンティティ作成時にキ?名が指定されていない場合、キ?名は自動生成されます。これらは、キ?スペ?スに均等に分散するように割り?てられます。
- カスタム名を使用するキ?では、常にスラッシュ(
/
)を除く UTF-8 文字を使用します。非 UTF-8 文字は、
Google BigQuery
への Cloud Datastore バックアップのインポ?トなど、さまざまな?理に影響します。また、スラッシュは??リリ?スされる機能に影響する可能性があります。
- ?値 ID を使用するキ?の場合:
- ID には負の?値を使用しないでください。負の ID は?べ替えに影響する場合があります。
- ID には値
0
(ゼロ)を使用しないでください。使用すると、自動的に ID が割り?てられます。
- 作成したエンティティに?して?自の?値 ID を手動で割り?てる場合は、アプリケ?ションで
allocateIds()
メソッドを使用して ID のブロックを取得します。こうすると、手動の?値 ID が別のエンティティに割り?てられなくなります。
-
作成したエンティティに?自の手動?値 ID またはカスタム名を割り?てる場合は、次のように?調に?加する値を使用しないでください。
1, 2, 3, …,
"Customer1", "Customer2", "Customer3", ….
"Product 1", "Product 2", "Product 3", ….
アプリケ?ションが大きなトラフィックを生成する場合、このような連?した番?がホットスポットを生み出し、Cloud Datastore のレイテンシに影響する可能性があります。連?した?値 ID の問題を回避するには、
allocateIds()
メソッドから?値 ID を取得します。
allocateIds()
メソッドを使用することで、適度に分散された順序の?値 ID が生成されます。+ キ?を指定するか生成された名前を格納することで、エンティティを見つけるためにクエリを?行する必要なしに、後でそのエンティティに?して一貫した
lookup()
を?行できます。
インデックス
プロパティ
- 文字列型
のプロパティには常に UTF-8 文字を使用します。文字列型のプロパティで非 UTF-8 文字を使用すると、クエリに影響する場合があります。非 UTF-8 文字でデ?タを保存する必要がある場合は
バイト型文字列
を使用します。
- プロパティ名にはドットを使用しないでください。プロパティ名にドットを使用すると、今後の
構造化プロパティ
のインデックス付けに影響する場合があります。
クエリ
- クエリ結果のキ?だけにアクセスする場合は、
キ?のみのクエリ
を使用します。キ?のみのクエリでは、エンティティ全?を取得する場合よりも低いレイテンシとコストで結果が返されます。
- エンティティの特定のプロパティだけにアクセスする場合は、
射影クエリ
を使用します。射影クエリでは、エンティティ全?を取得する場合よりも低いレイテンシとコストで結果が返されます。
- 同?に、クエリフィルタに含まれるプロパティ(たとえば
order by
句でリストされるもの)だけにアクセスする場合も
射影クエリ
を使用します。
- オフセットは使用しないでください。代わりに
カ?ソル
を使用します。オフセットを使用しても、スキップされたエンティティがアプリケ?ションに返されなくなるだけで、?部的にはそのようなエンティティも引き?き取得されます。スキップされたエンティティはクエリのレイテンシに影響し、そのようなエンティティの取得に必要な?み取りオペレ?ションに?してアプリケ?ションに課金されます。
- クエリに?整合性が必要な場合は、
祖先クエリ
を使用します(祖先クエリを使用するには、まず
?整合性に??するデ?タ構造
にする必要があります)。祖先クエリは?整合性のある結果を返します。
キ?のみ
の非祖先クエリに?けて
lookup()
を?行しても、?整合性のある結果は返されません。これは、キ?のみの非祖先クエリはクエリの?行時点で整合性が保たれていないインデックスから結果を取得する可能性があるためです。
スケ?ルを考慮して設計する
?一エンティティ グル?プに?する更新
Cloud Datastore ?の 1 つのエンティティ グル?プだけを高い頻度で更新することは避けてください。
Cloud Datastore を使用するときは、
1 つのエンティティ グル?プの更新頻度が?秒 1 回以上となる必要がない
ようにアプリケ?ションを設計することをおすすめします。親も子も持たないエンティティは、それ自身がエンティティ グル?プとなることに留意してください。エンティティ グル?プの更新頻度が高すぎる場合は、Cloud Datastore の書き?みレイテンシが大きくなり、タイムアウトやその他のタイプのエラ?が?生します。このことを、競合(コンテンション)といいます。
?一エンティティ グル?プへの Cloud Datastore の書き?み頻度は、?秒 1 回という制限を超えることもあるため、負荷テストではこの問題が現れない可能性があります。エンティティ グル?プへの書き?み頻度を下げるようにアプリケ?ションを設計するための推?事項は、
Cloud Datastore の競合の記事
をご?ください。
?いキ?範?への高頻度での?み取りと書き?み
?書順が近い一連の Cloud Datastore キ?に?する、高頻度での?み取りや書き?みは避けてください。
Cloud Datastore は、Google の NoSQL デ?タベ?スである
Bigtable
の上に構築されており、Bigtable のパフォ?マンス特性に左右されます。Bigtable のスケ?リングは、それぞれ?立したタブレットに行を振り分けるという方法で行われますが、これらの行はキ?で?書順に?べられます。
Cloud Datastore を使用するときは、ホット タブレットが理由で書き?み速度が低下することがあります。これが起きるのは、小さい範?の一連のキ?に?する書き?み頻度が急に上昇し、1 つのタブレット サ?バ?の?理能力を超えた場合です。高負荷をサポ?トするために、最終的に Bigtable はキ?スペ?スを分割します。
?み取りの場合の上限は一般的に、書き?みの上限を大きく上回りますが、?一のキ?を高頻度で?み取る場合を除きます。Bigtable は、?一のキ?を複?のタブレットに分割することはできません。
ホット タブレットは、エンティティ キ?とインデックスの?方に使用されるキ?範?に?して?生することがあります。
場合によっては、小さい範?のキ?への?み取りや書き?みができなくなること以外にも、Cloud Datastore ホットスポットがアプリケ?ションに影響を及ぼすことがあります。たとえば、ホットキ?の?み取りや書き?みがインスタンス起動時に行われると、ロ?ド リクエストが失敗します。
デフォルトでは、Cloud Datastore によるキ?の割り?てには分散アルゴリズムが使用されます。したがって、新しいエンティティを作成するための書き?みが高頻度でも、デフォルトの ID 割り?てポリシ?を使用していれば、Cloud Datastore への書き?みでホットスポットが?生することは通常はありません。次のようなケ?スでは、この問題が?生することがあります。
-
新しいエンティティの作成をきわめて高頻度で行い、このときに以前の順次 ID 割り?てポリシ?を使用する場合。
-
新しいエンティティの作成をきわめて高頻度で行い、?自の ID を割り?てるが、その ID が?調に?加していく場合。
-
ある種類の新しいエンティティの作成をきわめて高頻度で行うが、その種類の?存のエンティティがごく少?であった場合。Bigtable は、最初はすべてのエンティティを同じタブレット サ?バ?に配置しますが、ある程度の時間が?過するとキ?範?を複?のタブレット サ?バ?に分割するようになります。
-
この問題は、新しいエンティティを高頻度で作成するときに、タイムスタンプのような、?調に?加していくインデックス付きプロパティがある場合にも?生します。このようなプロパティは、Bigtable のインデックス テ?ブル?の行のキ?であるためです。
-
Cloud Datastore は、ル?ト エンティティ グル?プの名前空間と種類を Bigtable 行キ?の前に付加します。新しい名前空間または種類への書き?みを開始するときに、トラフィックを徐?に?やしていかなかった場合は、ホットスポットが?生することがあります。
?調に?加していくキ?またはインデックス付きプロパティがある場合は、ランダムなハッシュを前に付加すると、キ?が確?に複?のタブレットにシャ?ディングされるようになります。
同?に、ソ?トまたはフィルタを使用して?調に?加する(または減少する)プロパティを照?する必要がある場合は、新しいプロパティにインデックスを付けることができます。?調な値の前には、デ?タセット全?のカ?ディナリティは高いが、?行するクエリの範??のすべてのエンティティに共通するような値を付加します。たとえば、タイムスタンプでエントリを照?したいが、一度に 1 人のユ?ザ?の結果だけを返す場合には、タイムスタンプの前にユ?ザ? ID を付加し、その新しいプロパティにインデックスを付けることができます。これにより、そのユ?ザ?に?するクエリを?行し、順序付きの結果を得ることを可能としつつ、ユ?ザ? ID の存在によってインデックス自?が適切にシャ?ディングされることを保?できます。
この問題の詳しい?明については、
Cloud Datastore での?調に?加する値の保存に?する Ikai Lan のブログ投稿
をご?ください。
トラフィックを?やしていく
Cloud Datastore の新しい種類やキ?スペ?スの新しい部分へのトラフィックは、徐?に?やしていきます。
Cloud Datastore の新しい種類へのトラフィックを徐?に?やしていくのは、トラフィックの?加に合わせて Bigtable がタブレットを分割できるよう十分な時間を確保するためです。Cloud Datastore の新しい種類に?するオペレ?ションは?秒 500 回を上限とし、その後でトラフィックを 5 分ごとに 50% ?やしていくことをおすすめします。理論上は、この?加スケジュ?ルを使用すると 90 分後に?秒 740,000 回までオペレ?ションを?やすことができます。書き?みがキ?範?全?に比較的均等に分散するよう注意してください。Google の SRE は、これを「500/50/5」ル?ルと呼んでいます。
このような徐?に?やしていくパタ?ンが特に重要になるのは、コ?ドを?更した結果として種類 A の使用が停止し、代わりに種類 B が使用されるという場合です。この移行を?純に?理するには、種類 A を?み取り、存在しなければ種類 B を?み取るようにコ?ドを?更しますが、この方法では、キ?スペ?スのごく小さい部分を使用する新しい種類へのトラフィックが突然?加するおそれがあります。キ?スペ?スが疎である場合は、Bigtable が?率的にタブレットを分割できない可能性があります。
これと同じ問題は、エンティティを移行した結果として同じ種類の中の別のキ?範?が使用される場合にも起きることがあります。
どのような方法でエンティティを新しい種類またはキ?に移行するかは、デ?タモデルによって異なります。次の例で示しているのは、「同時?み?み」と呼ばれる方法です。この方法が?際のデ?タに?して?果的かどうかは、ご自身で判?する必要があります。重要な考慮事項の 1 つとして、移行中の?列オペレ?ションによる費用面での影響があります。
最初に古いエンティティまたはキ?から?み取ります。見つからない場合は、新しいエンティティまたはキ?から?み取ります。存在しないエンティティを高頻度で?み取ると、ホットスポット?生につながる可能性があるため、負荷を徐?に?やしていくことが必要です。より良い方法としては、古いエンティティを新しいエンティティにコピ?してから古いほうを削除するというものがあります。同時?み?みを徐?に?やしていくと、新しいキ?スペ?スが適切に分割されます。
新しい種類への?み取りや書き?みを徐?に?やしていくための方法として考えられるのは、ユ?ザ? ID の決定論的ハッシュを使用して、新しいエンティティに書き?むユ?ザ?の割合をランダムに決めるというものです。ユ?ザ? ID ハッシュの結果が、ランダム??によっても、ユ?ザ?の行動によっても、偏ることがないようにしてください。
一方で、Dataflow ジョブを?行して、すべてのデ?タを古いエンティティまたはキ?から新しいほうにコピ?します。このバッチジョブでは順次キ?への書き?みを避けてください。Bigtable のホットスポットを防ぐためです。バッチジョブが完了すると、?み取りは新しい場所からのみ可能になります。
この方法に改良を加えるとすれば、一度に移行するユ?ザ?を小さいバッチにまとめるというものがあります。ユ?ザ? エンティティにフィ?ルドを追加して、そのユ?ザ?の移行ステ?タスを記?します。1 つのバッチとして移行するユ?ザ?を選ぶ基準となるのは、ユ?ザ? ID のハッシュです。Mapreduce または Dataflow のジョブで、そのバッチのユ?ザ?のキ?を移行します。移行進行中のユ?ザ?は、同時?み取りを使用します。
ロ?ルバックは簡?にはできないことに注意してください。できるようにするには、移行段階が完了するまでは新??方のエンティティに二重に書き?む必要があります。これを行うと、Cloud Datastore の使用料が?加します。
削除
小さいキ?範?の大量の Cloud Datastore エンティティを削除することは避けてください。
Bigtable は定期的にテ?ブルをリライトしますが、その目的は削除?みのエントリを除去することと、?み取りと書き?みの?理?率が向上するようにデ?タを再編成することです。このプロセスは「コンパクション」と呼ばれます。
大量の Cloud Datastore エンティティを削除するときに、そのキ?の範?が小さい場合は、コンパクションが完了するまでの間、インデックスのこの部分に?するクエリが?くなります。極端なケ?スでは、クエリの結果が返される前にタイムアウトする可能性があります。
エンティティの有?期限を表すために、タイムスタンプをインデックス付きフィ?ルドの値として使用するのは、不適切な方法です。有?期限切れのエンティティを取り出すには、このインデックス付きフィ?ルドに?してクエリを?行することになりますが、該?するキ?スペ?スは、最近削除されたエンティティのインデックス エントリのスペ?スと重なっている可能性が高くなります。
「分割クエリ」のパフォ?マンスを向上させるには、固定長の文字列を有?期限タイムスタンプの前に付加します。インデックスはこの文字列全?の順に?べられるので、同じタイムスタンプを持つ複?のエンティティがインデックスのキ?範?全?に分散されます。複?のクエリを同時に?行して各シャ?ドから結果を取得することができます。
有?期限タイムスタンプの問題を解決するための、より完成度の高い方法は、定期的に更新されるグロ?バル カウンタである「世代番?」を使用するというものです。この世代番?を有?期限タイムスタンプの前に付加すると、クエリの結果は世代番?、シャ?ド、タイムスタンプの順に?べ替えられます。古いエンティティの削除は、前の世代で行われます。削除されないエンティティは、その世代番?に 1 が加算されます。削除が完了したら、次の世代に進みます。古い世代に?するクエリのパフォ?マンスは、コンパクションが完了するまでの間は低くなります。削除するエンティティのリストを取得するためにインデックスに?するクエリを?行する前に、何世代かの完了を待つことが必要になることがあります。これは、結果整合性が理由で結果が欠落するリスクを?減するためです。
シャ?ディングとレプリケ?ション
Cloud Datastore のホットキ?に?しては、シャ?ディングまたはレプリケ?ションを使用します。
レプリケ?ションを使用するのは、キ?範?の一部分の?み取りを、Bigtable の許容限度を超えて高頻度で行う必要がある場合です。この方法を使用すると、同じエンティティのコピ?が N 個保存されるので、?一エンティティの場合に比べて?み取り頻度を N 倍にすることができます。
Bigtable の許容限度を超える高頻度でキ?範?の一部に書き?む必要がある場合は、シャ?ディングを使用できます。シャ?ディングによって 1 つのエンティティが小さく分割されます。この?明については、
カウンタの分割
の記事をご?ください。
シャ?ディングするときによくある誤りとしては、次のものがあります。
次のステップ