最耗時的有效查詢統計資料

「最耗時的有效查詢」,又稱為「執行時間最長的查詢」,是一份在資料庫中有效的查詢清單,並按照查詢執行時間排序。深入瞭解這些查詢,有助於在發生系統延遲和 CPU 使用率偏高的情況時,找出原因。

Spanner 提供內建資料表 SPANNER_SYS.OLDEST_ACTIVE_QUERIES,列出執行中的查詢 (包括包含 DML 陳述式的查詢),並依開始時間排序,以升冪排序。但不包含變更資料流查詢。

如果執行大量查詢,由於系統會對這類資料的收集作業強制執行記憶體限制,因此結果可能會限於部分查詢。因此,Spanner 提供額外的資料表 SPANNER_SYS.ACTIVE_QUERIES_SUMMARY,顯示所有有效查詢的摘要統計資料 (變更串流查詢除外)。您可以使用 SQL 陳述式,從這兩個內建資料表擷取資訊。

在本文件中,我們將說明這兩個資料表,並提供一些使用這些資料表的查詢範例,最後示範如何使用這些資料表,以協助減輕由活動查詢造成的問題。

可用性

SPANNER_SYS 資料只能透過 SQL 介面取得,例如:

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) 取消查詢。

查詢範例

您可以使用用戶端程式庫Google Cloud CLIGoogle Cloud 控制台執行下列 SQL 陳述式範例。

列出最久執行的查詢

下列查詢會傳回一份最久未執行的查詢清單,並按照查詢的開始時間排序。

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_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; 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; 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; 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; 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; 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_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; 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; ACjbPvaF3yKiNfxXFod2LPoFaXjKR759Bw1o34206vv0t7eOrD3wxZhu8U6ohQ

ACTIVE_QUERIES_SUMMARY

顧名思義,內建表格 SPANNER_SYS.ACTIVE_QUERIES_SUMMARY 會顯示所有有效查詢的摘要統計資料。如以下結構定義所示,系統會依據查詢的時間長度將查詢分為三個區塊或計數器:超過 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 秒的查詢數量。

一個查詢可能會被計入多個分類。舉例來說,如果某個查詢執行了 12 秒,就會計入 COUNT_OLDER_THAN_1SCOUNT_OLDER_THAN_10S,因為它符合兩個條件。

查詢範例

您可以使用用戶端程式庫gcloud spannerGoogle Cloud 主控台執行下列 SQL 陳述式範例。

擷取執行中查詢的摘要

下列查詢會傳回執行查詢的摘要統計資料。

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 秒。這在我們的資料庫中很不尋常,因此我們想進一步調查。

擷取執行中的查詢清單

我們在前一個步驟中判斷,有一個查詢執行時間超過 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 日下午約 16 點 44 分 9 秒 (美國東部時間) 執行查詢,並傳回下列結果。(您可能需要水平捲動畫面,才能查看完整輸出內容)。

start_time text_fingerprint 文字 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_QUERIESSPANNER_SYS.ACTIVE_QUERIES_SUMMARY 分析執行中的查詢,並視需要針對導致 CPU 用量過高的查詢採取行動。當然,避免高成本作業並為您的用途設計正確的結構定義,一向是較為省錢的做法。如要進一步瞭解如何建構可有效執行的 SQL 陳述式,請參閱 SQL 最佳做法

後續步驟