查詢執行計畫

總覽

本頁面提供查詢執行計畫的概念,以及 Spanner 如何運用這些計畫,在分散式環境中執行查詢。如要瞭解如何使用Google Cloud 主控台擷取特定查詢的執行計劃,請參閱「瞭解 Spanner 如何執行查詢」。您也可以查看取樣的歷來查詢計畫,並比較特定查詢在一段時間內的查詢效能。如需更多資訊,請參閱「取樣的查詢計畫」。

Spanner 使用宣告式 SQL 陳述式查詢資料庫。SQL 陳述式會定義使用者想要的「內容」,不會指定取得結果的「方法」。「查詢執行計劃」是一組如何取得結果的步驟。對於特定的 SQL 陳述式,取得結果的方式可能有很多種。Spanner 查詢最佳化工具會評估不同的執行計畫,並選擇認為最有效率的計畫。接著,Spanner 便會使用該執行計劃來擷取結果。

就概念上而言,執行計劃就是相關運算子的樹狀結構。每個運算子都會從輸入讀取資料列,並產生輸出的資料列。系統會傳回執行作業根部的運算子結果做為 SQL 查詢的結果。

此查詢的範例為:

SELECT s.SongName FROM Songs AS s;

查詢執行計劃的結果看起來像這樣:

範例查詢執行計劃

本頁的查詢和執行計畫是根據下列資料庫結構定義:

CREATE TABLE Singers (
  SingerId   INT64 NOT NULL,
  FirstName  STRING(1024),
  LastName   STRING(1024),
  SingerInfo BYTES(MAX),
  BirthDate  DATE
) PRIMARY KEY(SingerId);

CREATE INDEX SingersByFirstLastName ON Singers(FirstName, LastName);

CREATE TABLE Albums (
  SingerId        INT64 NOT NULL,
  AlbumId         INT64 NOT NULL,
  AlbumTitle      STRING(MAX),
  MarketingBudget INT64
) PRIMARY KEY(SingerId, AlbumId),
  INTERLEAVE IN PARENT Singers ON DELETE CASCADE;

CREATE INDEX AlbumsByAlbumTitle ON Albums(AlbumTitle);

CREATE INDEX AlbumsByAlbumTitle2 ON Albums(AlbumTitle) STORING (MarketingBudget);

CREATE TABLE Songs (
  SingerId  INT64 NOT NULL,
  AlbumId   INT64 NOT NULL,
  TrackId   INT64 NOT NULL,
  SongName  STRING(MAX),
  Duration  INT64,
  SongGenre STRING(25)
) PRIMARY KEY(SingerId, AlbumId, TrackId),
  INTERLEAVE IN PARENT Albums ON DELETE CASCADE;

CREATE INDEX SongsBySingerAlbumSongNameDesc ON Songs(SingerId, AlbumId, SongName DESC), INTERLEAVE IN Albums;

CREATE INDEX SongsBySongName ON Songs(SongName);

CREATE TABLE Concerts (
  VenueId      INT64 NOT NULL,
  SingerId     INT64 NOT NULL,
  ConcertDate  DATE NOT NULL,
  BeginTime    TIMESTAMP,
  EndTime      TIMESTAMP,
  TicketPrices ARRAY<INT64>
) PRIMARY KEY(VenueId, SingerId, ConcertDate);

您可以使用下列資料操縱語言 (DML) 陳述式,將資料新增至這些資料表:

INSERT INTO Singers (SingerId, FirstName, LastName, BirthDate)
VALUES (1, "Marc", "Richards", "1970-09-03"),
       (2, "Catalina", "Smith", "1990-08-17"),
       (3, "Alice", "Trentor", "1991-10-02"),
       (4, "Lea", "Martin", "1991-11-09"),
       (5, "David", "Lomond", "1977-01-29");

INSERT INTO Albums (SingerId, AlbumId, AlbumTitle)
VALUES (1, 1, "Total Junk"),
       (1, 2, "Go, Go, Go"),
       (2, 1, "Green"),
       (2, 2, "Forever Hold Your Peace"),
       (2, 3, "Terrified"),
       (3, 1, "Nothing To Do With Me"),
       (4, 1, "Play");

INSERT INTO Songs (SingerId, AlbumId, TrackId, SongName, Duration, SongGenre)
VALUES (2, 1, 1, "Let's Get Back Together", 182, "COUNTRY"),
       (2, 1, 2, "Starting Again", 156, "ROCK"),
       (2, 1, 3, "I Knew You Were Magic", 294, "BLUES"),
       (2, 1, 4, "42", 185, "CLASSICAL"),
       (2, 1, 5, "Blue", 238, "BLUES"),
       (2, 1, 6, "Nothing Is The Same", 303, "BLUES"),
       (2, 1, 7, "The Second Time", 255, "ROCK"),
       (2, 3, 1, "Fight Story", 194, "ROCK"),
       (3, 1, 1, "Not About The Guitar", 278, "BLUES");

由於 Spanner 會將資料區分成拆分,因此取得有效率的執行計畫會變得很有挑戰性。拆分可獨立移動並指派到位於不同實體地點的不同伺服器。為了評估分散資料的執行計劃,Spanner 會依據下列條件採用執行作業:

  • 在包含資料的伺服器中,在本機執行「子計劃」
  • 使用積極分散式縮減,自動化調度管理與匯總多項遠端執行作業

Spanner 會使用原始運算子 distributed union 和其變體 distributed cross applydistributed outer apply 來啟用這個模型。

取樣的查詢計畫

您可以使用 Spanner 取樣的查詢計畫,查看歷史查詢計畫的樣本,並比較查詢的長期成效。並非所有查詢都有可用的查詢計畫樣本。只有耗用較高 CPU 的查詢才會被取樣。Spanner 查詢計畫範例的資料保留期限為 30 天。您可以在 Google Cloud 控制台的「查詢深入分析」頁面中找到查詢計畫範例。如需操作說明,請參閱「查看取樣的查詢計劃」。

取樣查詢計畫的結構與一般查詢執行計畫相同。如要進一步瞭解如何解讀視覺化計畫,並使用這些計畫來偵錯查詢,請參閱「查詢計畫視覺化工具導覽」。

取樣查詢計畫的常見用途:

取樣查詢計畫的常見用途包括:

如果查詢的效能隨著時間出現明顯差異,或是您想改善查詢效能,請參閱 SQL 最佳做法,藉此建立最佳化查詢陳述式,協助 Spanner 找出效率較高的執行計畫。

查詢的生命週期

系統會將 Spanner 中的 SQL 查詢編譯成執行計劃,然後再傳送到初始「根」伺服器執行。系統會選擇使用最少躍點即可到達查詢資料的根伺服器。接著,根伺服器會:

  • 啟動子計劃的遠端執行 (如有必要)
  • 等待遠端執行的結果。
  • 處理任何剩餘的本機執行步驟,例如匯總結果
  • 傳回查詢的結果

接收到子計劃的遠端伺服器會做為其子計劃的「根」伺服器,並遵循頂層根伺服器的模式執行。結果會是遠端執行作業的樹狀結構。概念上,查詢執行流程是從上到下,而查詢結果則是從下到上傳回。下圖顯示此模式:

概念查詢計劃

以下範例會更清楚說明這個模式。

匯總查詢

匯總查詢會實作 GROUP BY 查詢。

例如使用這個查詢:

SELECT s.SingerId, COUNT(*) AS SongCount
FROM Songs AS s
WHERE s.SingerId < 100
GROUP BY s.SingerId;

結果會是:

+----------+-----------+
| SingerId | SongCount |
+----------+-----------+
|        3 |         1 |
|        2 |         8 |
+----------+-----------+

概念上,下方是執行計劃:

匯總查詢執行計劃

Spanner 會將執行計劃傳送到與查詢執行合作的根伺服器,然後執行子計劃的遠端分散作業。

此執行計畫以分散式聯集開始,將子計畫發送到拆分滿足 SingerId < 100 的遠端伺服器。個別分割區的掃描作業完成後,串流匯總運算子會匯總資料列,取得每個 SingerId 的計數。接著,serialize result 運算子會將結果序列化。最後,分散式聯集會將所有結果結合,然後傳回查詢結果。

如要進一步瞭解匯總,請參閱匯總運算子

共置彙整查詢

系統將交錯式資料表與其相關資料表的資料列儲存在同個位置。「共置彙整」指的是交錯資料表之間的彙整。共置彙整比需要索引或後端的彙整更具效能優勢。

例如使用這個查詢:

SELECT al.AlbumTitle, so.SongName
FROM Albums AS al, Songs AS so
WHERE al.SingerId = so.SingerId AND al.AlbumId = so.AlbumId;

(這個查詢假設 SongsAlbums 交錯)。

結果會是:

+-----------------------+--------------------------+
| AlbumTitle            | SongName                 |
+-----------------------+--------------------------+
| Nothing To Do With Me | Not About The Guitar     |
| Green                 | The Second Time          |
| Green                 | Starting Again           |
| Green                 | Nothing Is The Same      |
| Green                 | Let's Get Back Together  |
| Green                 | I Knew You Were Magic    |
| Green                 | Blue                     |
| Green                 | 42                       |
| Terrified             | Fight Story              |
+-----------------------+--------------------------+

以下為執行計畫:

共置彙整查詢執行計劃

此執行計畫從分散式聯集開始,將子計畫發布到具有 Albums 資料表拆分的遠端伺服器。由於 SongsAlbums 的交錯式資料表,每個遠端伺服器都能在各個遠端伺服器執行整個子計劃,不需要與不同的伺服器彙整。

子計劃包含交叉套用。每個交叉套用都會在資料表 Albums 上執行資料表 掃描,以擷取 SingerIdAlbumIdAlbumTitle。然後,交叉套用會將資料表掃描的輸出,對應到 SongsBySingerAlbumSongNameDesc 索引的索引掃描輸出,並在符合資料表掃描輸出的 SingerId 的索引中,套用 SingerId篩選器。每個交叉套用都會將結果傳送到序列化結果運算子,將 AlbumTitleSongName 資料序列化,再將結果傳回本機的分散式聯集。分散式聯集會匯總本機分散式聯集的結果,接著傳回這些結果做為查詢結果。

索引與後端彙整查詢

以上範例使用兩個資料表的彙整,其中一個資料表與另一個資料表交錯。當兩個資料表沒有交錯,或一個資料表和一個索引沒有交錯時,執行計劃會較複雜,效率也比較差。

建議使用以下指令建立索引:

CREATE INDEX SongsBySongName ON Songs(SongName)

請在下方查詢中使用這個索引:

SELECT s.SongName, s.Duration
FROM Songs@{force_index=SongsBySongName} AS s
WHERE STARTS_WITH(s.SongName, "B");

結果會是:

+----------+----------+
| SongName | Duration |
+----------+----------+
| Blue     |      238 |
+----------+----------+

以下為執行計畫:

後端彙整查詢執行計劃

由於 SongsBySongName 索引不包含 Duration 資料欄,而使產生的執行計劃變得複雜。為了取得 Duration 值,Spanner 需要後端彙整索引結果到 Songs 資料表。這僅是彙整,不是共置,因為 Songs 資料表與全域索引 SongsBySongName 並未交錯。產生的查詢計劃會比共置彙整範例更複雜,是因為 Spanner 在資料位於不同位置時會執行最佳化以加速執行作業。

頂層的運算子是分散式交叉套用。此運算子的輸入端是來自 SongsBySongName 索引的資料列批次,滿足述詞 STARTS_WITH(s.SongName, "B")。接著分散式交叉套用會將這些批次對應到拆分中包含 Duration 資料的遠端伺服器。遠端伺服器使用資料表掃描來擷取 Duration 資料欄。資料表掃描使用 Condition:($Songs_key_TrackId' = $batched_Songs_key_TrackId) 篩選器,將 Songs 資料表的 TrackId 彙整到來自 SongsBySongName 索引批次的資料列 TrackId

系統會匯總結果取得最終的查詢答案。另一方面,分散式交叉套用的輸入端會包含分散式聯集/本機分散式聯集組合,評估滿足述詞 STARTS_WITH 的索引資料列。

建議執行有些不同的查詢,不選用 s.Duration 欄:

SELECT s.SongName
FROM Songs@{force_index=SongsBySongName} AS s
WHERE STARTS_WITH(s.SongName, "B");

此查詢作業可完整發揮索引的功能,如下方執行計劃所示:

簡易查詢執行計劃

由於查詢要求的所有資料欄都在索引中,此執行計劃不需要後端彙整。

後續步驟