Spanner には、読み取りに関する統計情報を保存する組み込みテーブルが用意されています。SQL ステートメントを使用すると、これらの SPANNER_SYS.READ_STATS* テーブルから統計情報を取得できます。
読み取りの統計情報を使用するタイミング
読み取りの統計情報からは、アプリケーションにおけるデータベースの使用状況に関する分析情報を得ることができます。これは、パフォーマンスの問題を調査する際に有用です。たとえば、データベースで実行されている読み取り形状とその実行頻度を確認することで、この読み取り形状のパフォーマンス特性を把握できます。データベースに関する読み取りの統計情報を使用して、CPU 使用率が上昇する読み取り形状を特定できます。大まかに言うと、読み取りの統計情報は、リソースの使用状況の観点からデータベースに送信されるトラフィックの動作を理解するうえで有用です。
制限事項
- これは、大半の CPU 使用率を占める類似の読み取りストリームを分析する場合に最適なツールです。1 回だけ実行された読み取りの検索には適していません。 
- これらの統計情報で追跡されている CPU 使用率は、プリフェッチの CPU 使用率とその他のオーバーヘッドを除く、Spanner サーバー側の CPU 使用率を表しています。 
- 統計情報は、ベスト エフォート ベースで収集されます。そのため、基盤となるシステムに問題がある場合、統計情報が失われる可能性があります。たとえば、内部ネットワークに問題がある場合、一部の統計情報が失われる可能性があります。 
読み取りの統計情報にアクセスする
Spanner は、SPANNER_SYS スキーマに読み取り統計情報を提供します。SPANNER_SYS データにアクセスするには、次の方法を使用できます。
- Google Cloud コンソールのデータベースの Spanner Studio ページ 
- gcloud spanner databases execute-sqlコマンド
- executeSqlメソッドまたは- executeStreamingSqlメソッド。
Spanner が提供する次の単一読み取りメソッドは、SPANNER_SYS をサポートしていません。
- 強力な読み取りを実行してテーブルから単一行または複数行を読み取る。
- ステイル読み取りを実行してテーブルから単一行または複数行を読み取る。
- セカンダリ インデックスから単一行または複数行を読み取る。
CPU 使用率(読み取り形状別)
次のテーブルでは、特定の期間中に CPU 使用率が最も高い読み取り形状が追跡されます。
- SPANNER_SYS.READ_STATS_TOP_MINUTE: 1 分間隔で集計された読み取り形状の統計情報。
- SPANNER_SYS.READ_STATS_TOP_10MINUTE: 10 分間隔で集計された読み取り形状の統計情報。
- SPANNER_SYS.READ_STATS_TOP_HOUR: 1 時間間隔で集計された読み取り形状の統計情報。
これらのテーブルには以下のプロパティがあります。
- 各テーブルには、テーブル名で指定された長さの、時間間隔が重複しないデータが含まれています。 
- 時間間隔は時刻に基づきます。1 分間隔のサイクルは毎分始まり、10 分間隔と 1 時間間隔は正時から始まります。各間隔が経過すると、Spanner はすべてのサーバーからデータを収集し、その後すぐに SPANNER_SYS テーブルでデータを使用できるようにします。 - たとえば、午前 11 時 59 分 30 秒に SQL クエリで使用可能な最新の時間間隔は次のとおりです。 - 1 分: 午前 11:58:00~11:58:59
- 10 分: 午前 11:40:00~11:49:59
- 1 時間: 午前 10:00:00~10:59:59
 
- Spanner は統計情報を読み取り形状でグループ化します。タグが存在する場合、FPRINT はタグのハッシュです。存在しない場合は、 - READ_COLUMNS値のハッシュです。
- 各行には、指定された期間中に Spanner が統計情報をキャプチャした特定の読み取り形状のすべての実行に関する統計情報が含まれています。 
- Spanner が期間中に実行された個別の読み取り形状に関するすべての情報を保存できない場合は、指定された期間中に最も高い CPU 使用率を記録した読み取り形状が優先されます。 
テーブル スキーマ
| 列名 | 型 | 説明 | 
|---|---|---|
| INTERVAL_END | TIMESTAMP | その時間間隔に含まれている読み取りが実行された時間間隔の終わり。 | 
| REQUEST_TAG | STRING | この読み取りオペレーションの省略可能なリクエストタグ。タグの使用の詳細については、リクエストタグによるトラブルシューティングをご覧ください。同じタグ文字列を持つ複数の読み取りの統計情報は、タグ文字列に一致する REQUEST_TAG がある 1 行にグループ化されます。 | 
| READ_TYPE | STRING | 読み取りが PARTITIONED_READかREADかを示します。PartitionRead API から取得した partitionToken を使用した読み取りは、PARTITIONED_READ読み取りタイプで表され、その他の読み取り API はREADで表されます。 | 
| READ_COLUMNS | ARRAY<STRING> | 読み取られた一連の列。これらはアルファベット順です。 | 
| FPRINT | INT64 | 存在する場合は REQUEST_TAG値のハッシュで、存在しない場合はREAD_COLUMNS値のハッシュです。 | 
| EXECUTION_COUNT | INT64 | Cloud Spanner が一定期間内に読み取り形状を実行した回数。 | 
| AVG_ROWS | FLOAT64 | 読み取りから返された行数の平均値。 | 
| AVG_BYTES | FLOAT64 | 送信エンコードのオーバーヘッドを除いた、読み取りから返されたデータの平均バイト数。 | 
| AVG_CPU_SECONDS | FLOAT64 | プリフェッチされた CPU とその他のオーバーヘッドを除いた、読み取りを実行する Spanner サーバー側の CPU 平均秒数。 | 
| AVG_LOCKING_DELAY_SECONDS | FLOAT64 | ロックを原因とする待機に費やした時間の平均秒数。 | 
| AVG_CLIENT_WAIT_SECONDS | FLOAT64 | Spanner が生成したデータをクライアントが直ちに使用しなかったことを原因とする待機に費やした時間の平均秒数。 | 
| AVG_LEADER_REFRESH_DELAY_SECONDS | FLOAT64 | すべての書き込みが観測された Paxos リーダーに対する確認を行うための待機に費やした時間の平均秒数。 | 
| RUN_IN_RW_TRANSACTION_EXECUTION_COUNT | INT64 | 読み取り / 書き込みトランザクションの一部として読み取りが実行された回数。この列は、読み取りを読み取り専用トランザクションに移動することで、ロック競合を回避できるかどうかを判断する際に有効です。 | 
| AVG_DISK_IO_COST | FLOAT64 | Spanner HDD のディスク負荷における、このクエリの平均費用。 この値は、データベースで実行する読み取りの相対的な HDD I/O 費用を比較するために使用します。HDD ストレージでデータをクエリすると、インスタンスの HDD ディスクの負荷容量に対して料金が発生します。値が高いほど、HDD ディスクの負荷が高く、SSD で実行する場合よりもクエリが遅くなる可能性があります。さらに、HDD ディスクの負荷が容量に達している場合、クエリのパフォーマンスがさらに影響を受ける可能性があります。インスタンスの HDD ディスク負荷容量の合計を比率としてモニタリングできます。HDD ディスクの負荷容量を増やすには、インスタンスに処理ユニットまたはノードを追加します。詳細については、コンピューティング容量を変更するをご覧ください。クエリのパフォーマンスを向上させるには、一部のデータを SSD に移動することも検討してください。 ディスク I/O を大量に消費するワークロードの場合は、頻繁にアクセスするデータを SSD ストレージに保存することをおすすめします。SSD からアクセスされたデータは、HDD ディスクの負荷容量を消費しません。頻繁にアクセスされないデータは HDD ストレージに保持しながら、必要に応じて選択したテーブル、列、セカンダリ インデックスを SSD ストレージに保存できます。詳細については、階層型ストレージの概要をご覧ください。 | 
クエリの例
このセクションには、読み取りの統計情報を取得する SQL ステートメントの例がいくつか含まれています。これらの SQL ステートメントは、クライアント ライブラリ、gcloud spanner、または Google Cloud コンソールを使用して実行できます。
特定の期間における各読み取り形状に関する基本統計を一覧表示する
次のクエリは、直前の 1 分間における上位の読み取り形状の元データを返します。
SELECT fprint,
       read_columns,
       execution_count,
       avg_cpu_seconds,
       avg_rows,
       avg_bytes,
       avg_locking_delay_seconds,
       avg_client_wait_seconds
FROM spanner_sys.read_stats_top_minute
ORDER BY interval_end DESC LIMIT 3;
クエリ出力
| fprint | read_columns | execution_count | avg_cpu_seconds | avg_rows | avg_bytes | avg_locking_delay_seconds | avg_client_wait_seconds | 
|---|---|---|---|---|---|---|---|
| 125062082139 | ["Singers.id", "Singers.name"] | 8514387 | 0.000661355290396507 | 310.79 | 205 | 8.3232564943763752e-06 | 0 | 
| 151238888745 | ["Singers.singerinfo"] | 3341542 | 6.5992827184280315e-05 | 12784 | 54 | 4.6859741349028595e-07 | 0 | 
| 14105484 | ["Albums.id", "Albums.title"] | 9306619 | 0.00017855774721667873 | 1165.4 | 2964.71875 | 1.4328191393074178e-06 | 0 | 
合計 CPU 使用率が高い順に読み取り形状を一覧表示する
次のクエリは、直近 1 時間の CPU 使用率が最も高い読み取り形状を返します。
SELECT read_columns,
       execution_count,
       avg_cpu_seconds,
       execution_count * avg_cpu_seconds AS total_cpu
FROM spanner_sys.read_stats_top_hour
WHERE interval_end =
  (SELECT MAX(interval_end)
   FROM spanner_sys.read_stats_top_hour)
ORDER BY total_cpu DESC LIMIT 3;
クエリ出力
| read_columns | execution_count | avg_cpu_seconds | total_cpu | 
|---|---|---|---|
| ["Singers.id", "Singers.name"] | 1647 | 0.00023380297430622681 | 0.2579 | 
| ["Albums.id", "Albums.title"] | 720 | 0.00016738889440282034 | 0.221314999999999 | 
| ["Singers.singerinfo""] | 3223 | 0.00037764625882302246 | 0.188053 | 
統計情報の集計
SPANNER_SYS には、特定の期間に Spanner によってキャプチャされた読み取りの統計情報の集計を格納するテーブルも含まれています。
- SPANNER_SYS.READ_STATS_TOTAL_MINUTE: すべての読み取り形状の 1 分間隔の統計情報の集計。
- SPANNER_SYS.READ_STATS_TOTAL_10MINUTE: すべての読み取り形状の 10 分間隔の統計情報の集計。
- SPANNER_SYS.READ_STATS_TOTAL_HOUR: すべての読み取り形状の 1 時間間隔の統計情報の集計。
統計情報の集計テーブルには次のプロパティがあります。
- 各テーブルには、テーブル名で指定された長さの、時間間隔が重複しないデータが含まれています。 
- 時間間隔は時刻に基づきます。1 分間隔のサイクルは毎分始まり、10 分間隔と 1 時間間隔は正時から始まります。 - たとえば、午前 11 時 59 分 30 秒の時点で、読み取りの統計情報の集計に対する SQL クエリで使用可能な最新の間隔は次のとおりです。 - 1 分: 午前 11:58:00~11:58:59
- 10 分: 午前 11:40:00~11:49:59
- 1 時間: 午前 10:00:00~10:59:59
 
- 各行には、指定した期間中にデータベースに対して実行されたすべての読み取り形状の統計情報が集計されています。時間間隔ごとに 1 行のみとなります。 
- SPANNER_SYS.READ_STATS_TOTAL_*テーブルにキャプチャされた統計情報には、Spanner が- SPANNER_SYS.READ_STATS_TOP_*テーブルにキャプチャしなかった読み取り形状が含まれている場合があります。
- これらのテーブルの一部の列は、Cloud Monitoring で指標として公開されます。公開される指標は次のとおりです。 - 返された行数
- 読み取り実行回数
- 読み取りの CPU 時間
- ロックされるまでの時間
- クライアントの待機時間
- リーダーが更新されるまでの時間
- 返されたバイト数
 - 詳細については、Spanner の指標をご覧ください。 
テーブル スキーマ
| 列名 | 型 | 説明 | 
|---|---|---|
| INTERVAL_END | TIMESTAMP | 含まれている読み取り形状が実行された時間間隔の終わり。 | 
| EXECUTION_COUNT | INT64 | Cloud Spanner が一定期間内に読み取り形状を実行した回数。 | 
| AVG_ROWS | FLOAT64 | 読み取りから返された行数の平均値。 | 
| AVG_BYTES | FLOAT64 | 送信エンコードのオーバーヘッドを除いた、読み取りから返されたデータの平均バイト数。 | 
| AVG_CPU_SECONDS | FLOAT64 | プリフェッチされた CPU とその他のオーバーヘッドを除いた、読み取りを実行する Spanner サーバー側の CPU 平均秒数。 | 
| AVG_LOCKING_DELAY_SECONDS | FLOAT64 | ロックを原因とする待機に費やした時間の平均秒数。 | 
| AVG_CLIENT_WAIT_SECONDS | FLOAT64 | スロットリングを原因とする待機に費やした時間の平均秒数。 | 
| AVG_LEADER_REFRESH_DELAY_SECONDS | FLOAT64 | マルチリージョン構成のインスタンス間における読み取りの調整に費やした時間の平均秒数。 | 
| RUN_IN_RW_TRANSACTION_EXECUTION_COUNT | INT64 | 読み取り / 書き込みトランザクションの一部として読み取りが実行された回数。この列は、一部の読み取りを読み取り専用トランザクションに移動することで、ロック競合を回避できるかどうかを判断する際に有効です。 | 
クエリの例
このセクションには、読み取りの統計情報の集計を取得する SQL ステートメントの例がいくつか含まれています。これらの SQL ステートメントは、クライアント ライブラリ、gcloud spanner、または Google Cloud コンソールを使用して実行できます。
すべての読み取り形状の合計 CPU 使用率を確認する
次のクエリは、直近 1 時間で読み取り形状によって消費された CPU 時間数を返します。
SELECT (avg_cpu_seconds * execution_count / 60 / 60)
  AS total_cpu_hours
FROM spanner_sys.read_stats_total_hour
WHERE interval_end =
  (SELECT MAX(interval_end)
   FROM spanner_sys.read_stats_total_hour);
クエリ出力
| total_cpu_hours | 
|---|
| 0.00026186111111111115 | 
特定の期間における合計実行回数を確認する
次のクエリは、直近 1 分間に実行された読み取り形状の総数を返します。
SELECT interval_end,
       execution_count
FROM spanner_sys.read_stats_total_minute
WHERE interval_end =
  (SELECT MAX(interval_end)
   FROM spanner_sys.read_stats_total_minute);
クエリ出力
| interval_end | execution_count | 
|---|---|
| 2020-05-28 11:02:00-07:00 | 12861966 | 
データの保持
Spanner は最低でも、次の期間中に各テーブルのデータを保持します。
- SPANNER_SYS.READ_STATS_TOP_MINUTEおよび- SPANNER_SYS.READ_STATS_TOTAL_MINUTE: 過去 6 時間を対象とする間隔。
- SPANNER_SYS.READ_STATS_TOP_10MINUTEおよび- SPANNER_SYS.READ_STATS_TOTAL_10MINUTE: 過去 4 日間を対象とするインターフェース。
- SPANNER_SYS.READ_STATS_TOP_HOURおよび- SPANNER_SYS.READ_STATS_TOTAL_HOUR: 過去 30 日間を対象とする間隔。
読み取りの統計情報を使用して高 CPU 使用率のトラブルシューティングを行う
Spanner の読み取り統計情報は、Spanner データベースで CPU 使用率が高いことについて調査する必要がある場合や、データベースで CPU 使用率の高い読み取り形状を把握する場合に有用です。大量のデータベース リソースを使用する読み取り形状を検査することは、Spanner ユーザーが運用コストを削減することにつながり、場合によっては一般的なシステム レイテンシを改善する方法として活用することが考えられます。次の手順では、読み取りの統計情報を使用してデータベースにおける高 CPU 使用率を調査します。
調査期間を選択する
まず、アプリケーションでの CPU 使用率が高くなり始めた時間を探すことから始めます。たとえば、次のシナリオでは、2020 年 5 月 28 日午後 5 時 20 分頃に問題が発生し始めました。
選択した期間の読み取りの統計情報を収集する
調査を開始する期間が指定されている場合は、その時刻の前後に READ_STATS_TOTAL_10MINUTE テーブルで収集された統計情報を確認いたします。このクエリの結果は、その期間内における CPU などの読み取り統計情報の変化の手がかりになる可能性があります。次のクエリは、4:30 pm から 7:30 pm まで(両端を含む)の読み取り統計情報の集計結果を返します。
SELECT
  interval_end,
  ROUND(avg_cpu_seconds,4) as avg_cpu_seconds,
  execution_count,
  avg_locking_delay_seconds
FROM SPANNER_SYS.READ_STATS_TOTAL_10MINUTE
WHERE
  interval_end >= "2020-05-28T16:30:00"
  AND interval_end <= "2020-05-28T19:30:00"
ORDER BY interval_end;
クエリから返される結果の例を次に示します。
| interval_end | avg_cpu_seconds | execution_count | avg_locking_delay_seconds | 
|---|---|---|---|
| 2020-05-28 16:40:00-07:00 | 0.0004 | 11111421 | 8.3232564943763752e-06 | 
| 2020-05-28 16:50:00-07:00 | 0.0002 | 8815637 | 8.98734051776406e-05 | 
| 2020-05-28 17:00:00-07:00 | 0.0001 | 8260215 | 6.039129247846453e-06 | 
| 2020-05-28 17:10:00-07:00 | 0.0001 | 8514387 | 9.0535466616680686e-07 | 
| 2020-05-28 17:20:00-07:00 | 0.0006 | 13715466 | 2.6801485272173765e-06 | 
| 2020-05-28 17:30:00-07:00 | 0.0007 | 12861966 | 4.6859741349028595e-07 | 
| 2020-05-28 17:40:00-07:00 | 0.0007 | 3755954 | 2.7131391918005383e-06 | 
| 2020-05-28 17:50:00-07:00 | 0.0006 | 4248137 | 1.4328191393074178e-06 | 
| 2020-05-28 18:00:00-07:00 | 0.0006 | 3986198 | 2.6973481999639748e-06 | 
| 2020-05-28 18:10:00-07:00 | 0.0006 | 3510249 | 3.7577083563017905e-06 | 
| 2020-05-28 18:20:00-07:00 | 0.0004 | 3341542 | 4.0940589703795433e-07 | 
| 2020-05-28 18:30:00-07:00 | 0.0002 | 8695147 | 1.9914494947583975e-05 | 
| 2020-05-28 18:40:00-07:00 | 0.0003 | 11679702 | 1.8331461539001595e-05 | 
| 2020-05-28 18:50:00-07:00 | 0.0003 | 9306619 | 1.2527332321222135e-05 | 
| 2020-05-28 19:00:00-07:00 | 0.0002 | 8520508 | 6.2268448078447915e-06 | 
| 2020-05-28 19:10:00-07:00 | 0.0006 | 13715466 | 2.6801485272173765e-06 | 
| 2020-05-28 19:20:00-07:00 | 0.0005 | 11947323 | 3.3029114639321295e-05 | 
| 2020-05-28 19:30:00-07:00 | 0.0002 | 8514387 | 9.0535466616680686e-07 | 
上の図では、平均 CPU 時間 avg_cpu_seconds がハイライト表示された期間に上昇しているのがわかります。値が 2020-05-28 19:20:00 の interval_end では CPU 時間が増大しているため、その期間を選択して次の手順で詳細な調査を行います。
CPU 使用率が高い原因となる読み取り形状を確認する
もう少し掘り下げて、ここでは READ_STATS_TOP_10MINUTE テーブルに対してクエリを実行し、前の手順で選択した期間のデータを取得します。このクエリの結果は、高 CPU 使用率の原因となっている読み取り形状を特定するうえで有用です。
SELECT
  read_columns,
  ROUND(avg_cpu_seconds,4) as avg_cpu_seconds,
  execution_count,
  avg_rows
FROM SPANNER_SYS.READ_STATS_TOP_10MINUTE
WHERE
  interval_end = "2020-05-28T19:20:00"
ORDER BY avg_cpu_seconds DESC LIMIT 3;
次のデータはクエリから返される結果の例です。avg_cpu_seconds によってランキングされた上位 3 つの読み取り形状に関する情報を返しています。クエリで ROUND を使用して、avg_cpu_seconds の出力を小数点以下 4 桁に制限していることに注目してください。
| read_columns | avg_cpu_seconds | execution_count | avg_rows | 
|---|---|---|---|
| [TestHigherLatency._exists,TestHigherLatency.lang_status,TestHigherLatency.score,globalTagAffinity.shares]1 | 0.4192 | 1182 | 11650.42216582 | 
| [TestHigherLatency._exists,TestHigherLatency.lang_status,TestHigherLatency.likes,globalTagAffinity.score] | 0.0852 | 4 | 12784 | 
| [TestHigherLatency._exists,TestHigherLatency.lang_status,TestHigherLatency.score,globalTagAffinity.ugcCount] | 0.0697 | 1140 | 310.7921052631 | 
1 _exists は、特定の行が存在するかどうかを確認するために使用する内部フィールドです。
CPU 使用率が上昇する理由の一つには、いくつかの読み取り形状についてより高い頻度で実行(execution_count)を開始したことが考えられます。おそらく、読み取りによって返された平均行数(avg_rows)が増加しています。読み取り形状のこれらのプロパティのいずれからも原因が特定できない場合は、avg_locking_delay_seconds、avg_client_wait_seconds、avg_bytes などの他のプロパティを調べます。
高い CPU 使用率を低減するためのベスト プラクティスを適用する
前述の手順を完了したら、これらのベスト プラクティスのいずれかを適用することで、状況に対処することを検討してください。
- Spanner が期間内に読み取り形状を実行した回数は、測定値が妥当な結果、または問題の兆候のいずれであるかを示すベースラインを必要とする指標の良い例です。指標のベースラインを設定すると、通常の動作からの予期しない逸脱の原因を検出して調査できます。 
- ほとんどの場合に CPU 使用率が比較的安定しているものの、突発的な上昇が見られ、こうした上昇について類似したユーザー リクエストの突発的な増大またはアプリケーションの動作との相関関係が認められる場合は、すべて想定どおりに動作していることを示している可能性があります。 
- 次のクエリを試し、Spanner が各読み取り形状を実行した回数でランク付けした上位の読み取り形状を特定します。 - SELECT interval_end, read_columns, execution_count FROM SPANNER_SYS.READ_STATS_TOP_MINUTE ORDER BY execution_count DESC LIMIT 10;
- レイテンシについて、特にマルチリージョン インスタンス構成を使用している場合に、可能な限り低い読み取りレイテンシを特定するには、強力な読み取りの代わりにステイル読み取りを使用して、読み取りレイテンシの - AVG_LEADER_REFRESH_DELAY_SECONDSコンポーネントを減らすか、削除します。
- 読み取りしか行わず、読み取りは単一読み取りメソッドを使って表現できる場合、その単一読み取りメソッドを使う必要があります。単一読み取りは、読み取り / 書き込みトランザクションとは異なり、ロックしません。そのため、データの書き込みを行わない場合は、高コストの読み取り / 書き込みトランザクションよりも読み取り専用トランザクションを使用する必要があります。 
次のステップ
- 別のイントロスペクション ツールについて学習する。
- Spanner が各データベースについて、データベースの情報スキーマ テーブルに保存するその他の情報について学習する。
- Spanner に関する SQL のベスト プラクティスについて学習する。
- 高 CPU 使用率の調査について学習する。