最も古いアクティブなクエリ(長時間実行中のクエリとも呼ばれる)は、データベース内でアクティブなクエリのリストであり、クエリの実行時間順に並べられています。これらのクエリを分析することにより、システムのレイテンシと CPU 使用率が高い場合の原因を特定できます。
Spanner には、実行中クエリ(DML ステートメントを含むクエリなど)を開始時間別に昇順で一覧表示する組み込みテーブル SPANNER_SYS.OLDEST_ACTIVE_QUERIES が用意されています。このテーブルに変更ストリーム クエリは含まれていません。
現在大量のクエリが実行中されている場合、このデータの収集に関してシステムが適用するメモリの制約により、結果は合計クエリのサブセットに限定される可能性があります。そのため、Spanner には SPANNER_SYS.ACTIVE_QUERIES_SUMMARY という追加のテーブルがあり、すべてのアクティブなクエリ(変更ストリーム クエリは除く)の統計情報の概要が表示されます。SQL ステートメントを使用すると、これらの組み込みテーブルの両方から情報を取得できます。
このドキュメントでは、両方のテーブルについて説明し、これらのテーブルを使用するクエリの例を紹介します。また、アクティブなクエリによって発生する問題を軽減するのに役立つテーブルの使用方法を最後に紹介します。
対象
SPANNER_SYS データは SQL インターフェースを介してのみ使用できます。たとえば、次の場所から使用できます。
- Google Cloud コンソール(データベースの [Spanner Studio] ページ) 
- executeQueryAPI
Spanner が提供する他の単一読み取りメソッドは、SPANNER_SYS をサポートしていません。
OLDEST_ACTIVE_QUERIES
SPANNER_SYS.OLDEST_ACTIVE_QUERIES は、開始時間で並べ替えられたアクティブなクエリのリストを返します。現在大量のクエリが実行中されている場合、このデータの収集に関して Spanner が適用するメモリの制約により、結果は合計クエリのサブセットに限定される可能性があります。アクティブなすべてのクエリの統計情報の概要は、ACTIVE_QUERIES_SUMMARY で確認できます。
テーブル スキーマ
| 列名 | 型 | 説明 | 
|---|---|---|
| START_TIME | TIMESTAMP | クエリの開始時刻。 | 
| TEXT_FINGERPRINT | INT64 | フィンガープリントは、トランザクションに含まれるオペレーションのハッシュです。 | 
| TEXT | STRING | クエリ ステートメントのテキスト。 | 
| TEXT_TRUNCATED | BOOL | TEXTフィールドのクエリテキストが切り捨てられた場合は true、それ以外の場合は false です。 | 
| SESSION_ID | STRING | クエリを実行しているセッションの ID。これはオブザーバビリティに使用されます。 | 
| QUERY_ID。 | STRING | クエリの ID。この ID を CALL cancel_query(query_id)とともに使用して、クエリをキャンセルします。 | 
クエリの例
次の SQL ステートメントの例は、クライアント ライブラリ、Google Cloud CLI、または Google Cloud コンソールを使用して実行できます。
最も古い実行中クエリを一覧で表示する
次のクエリは、クエリを開始時刻で並べ替えて、最も古い実行中クエリのリストを返します。
SELECT start_time,
       text_fingerprint,
       text,
       text_truncated,
       session_id
FROM spanner_sys.oldest_active_queries
ORDER BY start_time ASC;
| start_time | text_fingerprint | text | text_truncated | session_id | 
|---|---|---|---|---|
| 2020-07-18T07:52:28.225877Z | -3426560921851907385 | SELECT a.SingerId, a.AlbumId, a.TrackId, b.SingerId as b_id, b.AlbumId as b_albumid, b.TrackId as b_trackId FROM Songs as a CROSS JOIN Songs as b; | False | ACjbPvYsucrtcffHrRK6aObeIjZf12tSUwOsim-g1WC3IhqF4epzICCQR3GCHw | 
| 2020-07-18T07:54:08.622081Z | -9206690983832919848 | SELECT a.SingerId, a.AlbumId, a.TrackId, a.SongName, s.FirstName, s.LastName FROM Songs as a JOIN Singers as s ON s.SingerId = a.SingerId WHERE STARTS_WITH(s.FirstName, 'FirstName') LIMIT 1000000; | False | ACjbPvaF3yKiNfxXFod2LPoFaXjKR759Bw1o34206vv0t7eOrD3wxZhu8U6ohQ | 
| 2020-07-18T07:54:08.631744Z | -105437553161169030 | SELECT a.SingerId, a.AlbumId, a.TrackId, a.SongName, s.FirstName, s.LastName FROM Songs as a JOIN Singers as s ON s.SingerId = a.SingerId WHERE a.SingerId > 7 LIMIT 1000000; | False | ACjbPvanq3MesDNT98t64KdKAz3TlDZoCC-zgW-FJn91cJHuczQ_cOFN_Hdflw | 
| 2020-07-18T07:54:08.720011Z | -9206690983832919848 | SELECT a.SingerId, a.AlbumId, a.TrackId, a.SongName, s.FirstName, s.LastName FROM Songs as a JOIN Singers as s ON s.SingerId = a.SingerId WHERE STARTS_WITH(s.FirstName, 'FirstName') LIMIT 1000000; | False | ACjbPvYIE2QHkhnmMXuAGpB4inK7yMnQjmYgQ9FoygKNaB5KCXu7Sf7f9aghYw | 
| 2020-07-18T07:54:08.731006Z | 6561582859583559006 | SELECT a.SingerId, a.AlbumId, a.TrackId, a.SongName, s.FirstName, s.LastName FROM Songs as a JOIN Singers as s ON s.SingerId = a.SingerId WHERE a.SingerId > 10 LIMIT 1000000; | False | ACjbPvYNZ06N2YyvwV0YMlSRBNDtXBqZEK-iAAyPFnFcTmshPvzWkhr034ud7w | 
最も古い実行中クエリのうち上位 2 つを一覧で表示する
前述のクエリを少し変えたこの例では、クエリの開始時間で並び替えられた上位 2 つの最も古い実行中クエリが返されます。
SELECT start_time,
       text_fingerprint,
       text,
       text_truncated,
       session_id
FROM spanner_sys.oldest_active_queries
ORDER BY start_time ASC LIMIT 2;
クエリ出力
| start_time | text_fingerprint | text | text_truncated | session_id | 
|---|---|---|---|---|
| 2020-07-18T07:52:28.225877Z | -3426560921851907385 | SELECT a.SingerId, a.AlbumId, a.TrackId, b.SingerId as b_id, b.AlbumId as b_albumid, b.TrackId as b_trackId FROM Songs as a CROSS JOIN Songs as b; | False | ACjbPvYsucrtcffHrRK6aObeIjZf12tSUwOsim-g1WC3IhqF4epzICCQR3GCHw | 
| 2020-07-18T07:54:08.622081Z | -9206690983832919848 | SELECT a.SingerId, a.AlbumId, a.TrackId, a.SongName, s.FirstName, s.LastName FROM Songs as a JOIN Singers as s ON s.SingerId = a.SingerId WHERE STARTS_WITH(s.FirstName, 'FirstName') LIMIT 1000000; | False | ACjbPvaF3yKiNfxXFod2LPoFaXjKR759Bw1o34206vv0t7eOrD3wxZhu8U6ohQ | 
ACTIVE_QUERIES_SUMMARY
その名前が示すように、組み込みテーブル SPANNER_SYS.ACTIVE_QUERIES_SUMMARY には、すべてのアクティブなクエリの統計情報の概要が表示されます。次のスキーマに示すように、クエリは経過時間ごとに 3 つのバケットまたはカウンタ(1 秒間以上、10 秒以上、100 秒以上のもの)に分類されます。
テーブル スキーマ
| 列名 | 型 | 説明 | 
|---|---|---|
| ACTIVE_COUNT | INT64 | 実行中のクエリの合計数。 | 
| OLDEST_START_TIME | TIMESTAMP | 最も古い実行中クエリの開始時刻の上限。 | 
| COUNT_OLDER_THAN_1S | INT64 | 1 秒以上経過したクエリの数。 | 
| COUNT_OLDER_THAN_10S | INT64 | 10 秒以上経過したクエリの数。 | 
| COUNT_OLDER_THAN_100S | INT64 | 100 秒以上経過したクエリの数。 | 
1 つのクエリは、これらのバケットの 1 つ以上としてカウントされる場合があります。たとえば、クエリが 12 秒間実行されている場合、両方の条件を満たしているため、COUNT_OLDER_THAN_1S と COUNT_OLDER_THAN_10S でカウントされます。
クエリの例
次の SQL ステートメントの例は、クライアント ライブラリ、gcloud spanner、または Google Cloud コンソールを使用して実行できます。
アクティブなクエリの概要を取得する
次のクエリは、実行中のクエリに関する統計情報の概要を返します。
SELECT active_count,
       oldest_start_time,
       count_older_than_1s,
       count_older_than_10s,
       count_older_than_100s
FROM spanner_sys.active_queries_summary;
クエリ出力
| active_count | oldest_start_time | count_older_than_1s | count_older_than_10s | count_older_than_100s | 
|---|---|---|---|---|
| 22 | 2020-07-18T07:52:28.225877Z | 21 | 21 | 1 | 
制限事項
最も包括的な分析を可能にすることが目標となっていますが、状況によっては、これらのテーブルに返されるデータにクエリが含まれていない場合があります。
- DML クエリ(UPDATE / INSERT / DELETE)は、ミューテーションの適用フェーズ中は含まれません。 
- 一時的なエラーが原因で再起動中の場合、クエリは含まれません。 
- 過負荷のサーバーや応答がないサーバーのクエリは含まれません。 
- OLDEST_ACTIVE_QUERIESは読み取り / 書き込みトランザクションでは使用できません。読み取り専用トランザクションでも、トランザクション タイムスタンプが無視されて、常に実行時の現在のデータが返されます。まれに、部分的な結果で- ABORTEDエラーが返されることがあります。その場合は、部分的な結果を破棄して、もう一度クエリを実行してください。
アクティブなクエリデータを使用して CPU 使用率が高い場合のトラブルシューティングを行う
クエリ統計とトランザクション統計は、Spanner データベースでのレイテンシのトラブルシューティング時に役立つ情報を提供します。これらのツールは、完了したクエリに関する情報を提供します。ただし、システムで実行されているクエリの情報が必要になる場合もあります。たとえば、CPU 使用率が高く、次の質問に答えたい場合を考えてみます。
- 現在実行中のクエリの数は?
- それはどのようなクエリですか?
- 長時間(100 秒以上)実行されているクエリの数は?
- クエリを実行しているのはどのセッションですか?
これらの質問の答えに応じて、次の操作を行うことができます。
- 直ちに解決できるようクエリを実行しているセッションを削除する。
- インデックスを追加してクエリのパフォーマンスを向上させる。
- クエリが定期的なバックグラウンド タスクに関連付けられている場合は、そのクエリの頻度を減らす。
- クエリを実行する権限を持たないユーザーまたはコンポーネントが実行しているクエリを特定する。
このチュートリアルでは、アクティブなクエリを調べ、行うべき操作(もしあれば)を決定します。
アクティブなクエリの概要を取得する
このシナリオの例では、通常の CPU 使用率を上回っているため、次のクエリを実行してアクティブなクエリの概要を返します。
SELECT active_count,
       oldest_start_time,
       count_older_than_1s,
       count_older_than_10s,
       count_older_than_100s
FROM spanner_sys.active_queries_summary;
クエリにより次の結果が生成されます。
| active_count | oldest_start_time | count_older_than_1s | count_older_than_10s | count_older_than_100s | 
|---|---|---|---|---|
| 22 | 2020-07-18T07:52:28.225877Z | 21 | 21 | 1 | 
100 秒を超えて実行されているクエリが 1 つあることがわかりました。これはデータベースとして異常であるため、さらに詳しく調査する必要があります。
アクティブなクエリの一覧を取得する
前のステップで、100 秒以上実行されているクエリがあることがわかりました。さらに詳しく調査するため、次のクエリを実行して、上位 5 件の最も古い実行中クエリに関する詳細情報を取得します。
SELECT start_time,
       text_fingerprint,
       text,
       text_truncated,
       session_id,
       query_id
FROM spanner_sys.oldest_active_queries
ORDER BY start_time ASC LIMIT 5;
この例では、2024 年 3 月 28 日のおよそ午後 4 時 44 分 9 秒(EDT)にクエリを実行し、次の結果が返されました(出力全体を確認するには、横方向にスクロールする必要な場合もあります)。
| start_time | text_fingerprint | text | text_truncated | session_id | query_id | 
|---|---|---|---|---|---|
| 2024-03-28 16:44:09.356939+00:00 | -2833175298673875968 | select * from spanner_sys.oldest_active_queries | false | ACjbPvYsucrtcffHrRK6aObeIjZf12tSUwOsim-g1WC3IhqF4epzICCQR3GCHw | 37190103859320827 | 
| 2020-07-18T07:52:28.225877Z | -3426560921851907385 | SELECT a.SingerId, a.AlbumId, a.TrackId, b.SingerId as b_id, b.AlbumId as b_albumid, b.TrackId as b_trackId FROM Songs as a CROSS JOIN Songs as b; | false | ACjbPvaF3yKiNfxXFod2LPoFaXjKR759Bw1o34206vv0t7eOrD3wxZhu8U6ohQ | 48946620525959556 | 
最も古いクエリ(フィンガープリントが -2833175298673875968)がテーブル内でハイライト表示されます。これはコストが高い CROSS JOIN ので、対処が必要です。
コストの高いクエリをキャンセルする
この例では、コストの高い CROSS JOIN を実行しているクエリが見つかったため、このクエリをキャンセルします。前のステップで取得したクエリ結果には query_id が含まれています。GoogleSQL の場合は次の CALL cancel_query(query_id) コマンドを、PostgreSQL の場合は spanner.cancel_query(query_id) コマンドを実行して、クエリをキャンセルできます。
GoogleSQL
CALL cancel_query(query_id)
PostgreSQL
CALL spanner.cancel_query(query_id)
たとえば、次の CALL ステートメントで、ID 37190103859320827 のクエリをキャンセルできます。
CALL cancel_query('37190103859320827')
クエリがキャンセルされたことを確認するには、spanner_sys.oldest_active_queries テーブルに対してクエリを実行する必要があります。
このチュートリアルでは、SPANNER_SYS.OLDEST_ACTIVE_QUERIES と SPANNER_SYS.ACTIVE_QUERIES_SUMMARY を使用して、実行中のクエリを分析し、CPU 使用率の上昇の原因となるクエリに対して必要に応じて対処する方法について説明します。コストのかかるオペレーションを回避し、ユースケースに適したスキーマを設計すれば、より低コストになります。効率的に実行される SQL ステートメントの作成方法については、SQL のベスト プラクティスをご覧ください。
次のステップ
- 別のイントロスペクション ツールについて確認する。
- Spanner が各データベースについて、データベースの情報スキーマ テーブルに保存するその他の情報について学習確認する。
- Spanner に関する SQL のベスト プラクティスについて確認する。