Spanner の期限超過エラーのトラブルシューティング

このページでは、Spanner の期限超過エラーの概要(状況、原因、トラブルシューティングと解決方法について説明します)について説明します。

Spanner API にアクセスすると、DEADLINE_EXCEEDED エラーが原因でリクエストが失敗することがあります。このエラーは、あらかじめ構成されているタイムアウト時間内にレスポンスが取得されなかったことを示します。

期限超過エラーは、Spanner インスタンスの過負荷、最適化されていないスキーマ、最適化されていないクエリなど、さまざまな理由で発生します。このページでは、期限超過エラーになる一般的なシナリオについて説明し、これらの問題を調査して解決する方法を示します。

Spanner の期限と再試行の考え方

Spanner の期限と再試行ルールは、他の多くのシステムと異なります。Spanner では、タイムアウト期限を、レスポンスが役に立つ最大時間として指定する必要があります。同じオペレーションを直ちに再試行することを目的として意図的に期限を短くすることは推奨されていません。オペレーションがいつまでも完了しない原因となるためです。この状況で、次の手法とオペレーションはおすすめしません。これらは望ましくない結果を招き、Spanner の内部再試行動作を駄目にします。

  • 短すぎる期限を設定する。これは、オペレーションが不測のレイテンシの増加に対する耐性を持たず、タイムアウトする前に完了できないことを意味します。代わりに、レスポンスが役に立つ時間の期限を設定します。

  • 長すぎる期限を設定し、その期限前にオペレーションをキャンセルする。これは、複数の再試行を発生させる原因となり、それぞれの試行で無駄が発生します。全体では、インスタンスの負荷がかなり大きくなる可能性があります。

期限超過エラーとは

Spanner クライアント ライブラリのいずれかを使用すると、基盤となる gRPC レイヤが通信、マーシャリング、マーシャリング解除、期限の適用を処理します。期限により、アプリケーションは、リクエストが完了するまで待機する時間を指定できます。この期間が経過すると、リクエストは期限超過エラーで終了します。

タイムアウト構成ガイドでは、サポートされている各 Spanner クライアント ライブラリで期限(またはタイムアウト)を指定する方法が説明されています。Spanner クライアント ライブラリでは、次の構成ファイルで定義されているデフォルトのタイムアウト ポリシー設定と再試行ポリシー設定が使用されます。

gRPC の期限の詳細については、gRPC と期限をご覧ください。

一般的な期限超過エラーを調査して解決する方法

次の種類の問題では、DEADLINE_EXCEEDED エラーが発生することがあります。

データアクセス API の問題

データアクセス API の問題を回避するには、特定のワークロード用に Spanner インスタンスを適切に構成する必要があります。以下のセクションでは、さまざまなデータアクセス API の問題を調査して解決する方法について説明します。

Spanner インスタンスの CPU 負荷を確認する

リクエストのレイテンシは、CPU 使用率が推奨されている正常なしきい値を超えると大幅に増加することがあります。Spanner の CPU 使用率は、Google Cloud コンソールのモニタリング コンソールで確認できます。インスタンスの CPU 使用率に基づいてアラートを作成することもできます。

解決策

インスタンスの CPU 使用率を下げる手順については、CPU 使用率を減少させるをご覧ください。

リクエストのエンドツーエンド レイテンシの内訳を確認する

リクエストがクライアントから Spanner サーバーを経て戻ってくる場合、複数のネットワーク ホップを経由する必要があります。すなわち、クライアント ライブラリから Google Front End(GFE)、GFE から Spanner API フロントエンド、Spanner API フロントエンドから Spanner データベースなどです。これらのステージのいずれかでネットワークに問題があると、期限超過エラーが表示されることがあります。

各ステージのレイテンシをキャプチャできます。詳細については、Spanner リクエストのレイテンシ ポイントをご覧ください。Spanner でレイテンシが発生する場所を確認するには、Spanner でレイテンシが発生する場所を特定するをご覧ください。

解決策

レイテンシの内訳を取得すると、指標を使用してレイテンシを診断し、その原因を把握し、解決策を見つけることができます。

Data API の問題

Spanner の Data API の最適でない使用パターンによって、期限超過エラーが発生することがあります。このセクションでは、こうした最適でない使用パターンを確認する方法について説明します。

コストの高いクエリを確認する

クライアント ライブラリで、構成済みのタイムアウト期限内に実行されない高コストのクエリを実行すると、期限超過エラーが発生することがあります。コストの高いクエリの例には、大きなテーブルの完全スキャン、複数の大きなテーブルのクロス結合、非キー列に対する述語によるクエリ実行(テーブル全体のスキャンも)などがあります(これらに限定されません)。

コストの高いクエリは、クエリの統計情報テーブルトランザクションの統計情報テーブルを使用して調査できます。平均読み取り行数、平均読み取りバイト数、平均スキャン行数など、実行に時間がかかっているクエリおよびトランザクションに関する情報がこれらのテーブルに表示されます。さらに、クエリ実行計画を生成して、クエリが実行される様子を詳しく調べることもできます。

解決策

クエリを最適化するには、SQL クエリのベスト プラクティス ガイドを使用してください。また、前述の統計テーブルや実行プランで取得したデータを使用して、クエリを最適化し、データベースのスキーマ変更を行うこともできます。これらのベスト プラクティスは、ステートメントの実行時間を短縮する効果があり、期限超過エラーの解消に役立ちます。

ロック競合を確認する

Spanner のトランザクションは commit するためにロックを取得する必要があります。スループットが高い状態で実行されているアプリケーションでは、同じリソースでトランザクションが競合することがあります。これが原因で、ロックを取得するための待ち時間が長くなり、全体的なパフォーマンスに影響を及ぼします。その結果、読み取りリクエストと書き込みリクエストで期限を超過する場合があります。

レイテンシが大きな読み取り / 書き込みトランザクションの根本原因は、ロックの統計表で確認できます。また、次のブログ投稿もご覧ください。ロックの統計情報テーブルで、ロックの待機時間が最も長い行キーを確認できます。

このロック競合のトラブルシューティング ガイドでは、ロック競合に関係する列にアクセスしているトランザクションを見つける方法が説明されています。また、トランザクション タグのトラブルシューティング ガイドを使用して、ロック競合に関係するトランザクションを見つけることもできます。

解決策

こちらのベスト プラクティスを適用して、ロック競合を減らします。さらに、単純な読み取りのユースケースでは、読み取り専用トランザクションを使用して書き込みとのロック競合を回避します。読み取りおよび書き込みトランザクションは、書き込みワークフロー用か、読み取り / 書き込みの混合ワークフロー用に予約する必要があります。これらの手順により、トランザクションの実行時間の全体的なレイテンシを改善し、期限超過エラーを減少させることができます。

最適化されていないスキーマを確認する

Spanner データベースに最適なデータベース スキーマを設計する前に、データベースで実行されるクエリの種類を検討する必要があります。スキーマが最適ではないと、一部のクエリを実行したときにパフォーマンスの問題が発生する可能性があります。こうしたパフォーマンスの問題により、構成した期限内にリクエストが完了しない場合があります。

解決策

最適なスキーマ設計は、データベースに対する読み取りと書き込みによって異なります。スキーマの仕様に関係なく、スキーマ デザインのベスト プラクティス ガイドと、SQL のベスト プラクティス ガイドに従ってください。これらのガイドに従うことで、最も一般的なスキーマ設計の問題を回避できます。パフォーマンス低下のその他の根本的な原因には以下のものが挙げられます。主キーの選択、テーブル・レイアウト(高速アクセスのためのインターリーブ・テーブルの使用を参照)、スキーマ設計(パフォーマンスのためのスキーマの最適化を参照)、Spanner インスタンス内に構成されたノードのパフォーマンス(Spanner パフォーマンスの概要を参照)。

ホットスポットを確認する

Spanner は分散型データベースであるため、スキーマの設計は、ホットスポットを防ぐことを考慮する必要があります。たとえば、単調増加する列を作成すると、ワークロードを均等に分散するために Spanner で使用できるスプリットの数が制限されます。こうしたボトルネックは、タイムアウトが発生する可能性があります。また、Key Visualizer を使用すると、ホットスポットによるパフォーマンス問題のトラブルシューティングできます。

解決策

この問題を解決するには、最初のステップとして前のセクション最適化されていないスキーマを確認するで特定された解決策をご覧ください。データベース スキーマを再設計し、インターリーブされたインデックスを使用して、ホットスポット化を引き起こす可能性のあるインデックスを回避します。上記の手順で問題が解決されない場合は、ホットスポットを防ぐ主キーの選択ガイドをご覧ください。最後に、負荷ベースの分割を防止する可能性がある、最適でないトラフィック パターン(広い範囲の読み取りなど)を回避します。

誤って構成されているタイムアウトを確認する

クライアント ライブラリには、Spanner のすべてのリクエストに適切なタイムアウトのデフォルト値が用意されています。ただし、これらのデフォルト構成は、特定のワークロードに合わせて調整する必要が生じる場合もあります。クエリのコストを確認し、特定のユースケースに合わせて期限を調整することをおすすめします。

解決策

大半のユースケースには、デフォルトのタイムアウト設定で問題ありません。ユーザーはこれらの構成をオーバーライドできますが(カスタム タイムアウトと再試行ガイドを参照)、デフォルトの構成よりも積極的なタイムアウトを使用することはおすすめしません。タイムアウトを変更する場合は、アプリケーションが結果を待てる実際の時間を設定します。より長いタイムアウトを構成しても構いませんが、タイムアウトは、アプリケーションが待てる実際の時間よりも短くしないでください。短く設定すると、オペレーションの再試行頻度が高くなります。

Admin API の問題

Admin API リクエストは、Data API リクエストと比較してコストがかかるオペレーションです。管理者リクエスト(CreateInstanceCreateDatabaseCreateBackups など)は、レスポンスを返すまでに数秒かかることがあります。Spanner クライアント ライブラリでは、インスタンス管理リクエストとデータベース管理リクエストの両方に対して 60 分の期限が設定されます。これは、クライアントが再試行または失敗する前に、サーバーでリクエストを完了できるようにするための措置です。

解決策

Spanner クライアント ライブラリを使用して Admin API にアクセスする場合は、クライアント ライブラリが更新され、最新バージョンが使用されていることを確認してください。作成したクライアント ライブラリを介して Spanner API に直接アクセスする場合は、インスタンスデータベース管理リクエストのデフォルト設定(60 分)よりも積極的な期限設定でないことを確認してください。

Google Cloud コンソールに関する問題

Google Cloud コンソール Spanner Studio ページから発行されたクエリは 5 分を超えることはできません。実行に 5 分以上かかる高コストのクエリを作成すると、次のエラー メッセージが表示されます。

Google Cloud コンソールの期限超過のエラー メッセージのスクリーンショット

バックエンドが失敗したクエリをキャンセルし、必要に応じてトランザクションをロールバックする可能性があります。

解決策

SQL クエリのベスト プラクティス ガイドを使用して、クエリを書き換えます。

Dataflow の問題

Apache Beam では、読み取りオペレーションのデフォルトのタイムアウト構成が 2 時間となっており、commit オペレーションでは 15 秒になっています。これらの構成は、スタンドアロン クライアント ライブラリの期限タイムアウトよりも長い時間オペレーションを行うためのものです。ただし、作業項目が大きくなりすぎると、タイムアウトと期限超過エラーが発生する可能性があります。必要に応じて、Apache Beam commit のタイムアウト構成をカスタマイズできます。

解決策

ステップ ReadFromSpanner / Execute query / Read from Spanner / Read from Partitions で期限超過エラーが発生した場合は、クエリ統計情報テーブルを確認して、多数の行をスキャンしたクエリを見つけます。次に、そのようなクエリを変更して、実行時間を短縮します。

Dataflow の期限超過エラーの別の例を、次の例外メッセージで示します。

exception:
     org.apache.beam.sdk.util.UserCodeException:
     com.google.cloud.spanner.SpannerException: DEADLINE_EXCEEDED:
     io.grpc.StatusRuntimeException: DEADLINE_EXCEEDED: deadline exceeded after
     3599.999905380s.
     [remote_addr=batch-spanner.googleapis.com/172.217.5.234:443] at
 org.apache.beam.runners.dataflow.worker.GroupAlsoByWindowsParDoFn$1.output(GroupAlsoByWindowsParDoFn.java:184)

このタイムアウトは、作業項目が大きすぎるため発生しました。上記の例では、次の 2 つの推奨事項が役立つ可能性があります。まず、シャッフル サービスが有効になっていない場合は、有効にしてみてください。次に、データベースの読み取りにおける構成(maxPartitionspartitionSizeBytes など)を微調整します。詳細については、PartitionOptions を参照し、作業項目のサイズを減らしてください。これを行う方法の例については、この Dataflow テンプレートをご覧ください。

期限超過をトラブルシューティングする別のリソース

トラブルシューティング手順を完了しても DEADLINE_EXCEEDED エラーが引き続き表示される場合は、次のシナリオが発生した場合にサポートケースを開いてください

  • Google Front End のレイテンシは高いが、Spanner API リクエストのレイテンシは低い
  • Spanner API リクエストのレイテンシは高いが、クエリ レイテンシは低い

以下のトラブルシューティング リソースもご覧ください。