工作階段

本頁說明 Spanner 中工作階段的進階概念,包含建立用戶端程式庫、使用 REST 或 RPC API,或使用 Google 用戶端程式庫時工作階段的最佳做法。

工作階段代表著與 Spanner 資料庫服務的通訊管道。它可以用來執行讀取、寫入或修改 Spanner 資料庫中資料的交易。每個工作階段都適用於單一資料庫。

工作階段一次可執行一或多筆交易。執行多筆交易時,工作階段稱為「多工工作階段」。獨立的讀取、寫入和查詢作業會使用內部的交易。

工作階段集區的效能優勢

建立工作階段的價格高昂。為了避免每次執行資料庫作業產生的效能成本,用戶端應保留「工作階段集區」,也就是隨時可用的工作階段集區。集區應儲存現有的工作階段、收到要求時傳回適當的工作階段類型,並且清除未使用的工作階段。如要取得如何執行工作階段集區的範例,請參閱任一 Spanner 用戶端程式庫的原始碼,例如 Go 用戶端程式庫Java 用戶端程式庫

工作階段是為長期使用做準備,因此當用於資料庫作業之後,用戶端應將該工作階段傳回集區以重複使用。

gRPC 管道總覽

Spanner 用戶端會使用 gRPC 管道進行通訊。一個 gRPC 管道大致相當於一個 TCP 連線。一個 gRPC 管道最多可處理 100 個並行要求。也就是說,應用程式需要至少與應用程式執行的並行要求數量除以 100 相同的 gRPC 管道數量。

建立 Spanner 用戶端時,系統會建立 gRPC 通道集區。

使用 Google 用戶端程式庫的最佳做法

下列各節將說明使用 Spanner 適用的 Google 用戶端程式庫時的最佳做法。

設定集區中的工作階段和 gRPC 通道數量

用戶端程式庫在工作階段集區中設有預設工作階段數量,在管道集區中則設有預設 gRPC 管道數量。這兩個預設值在大多數情況下都適用。下表列出各程式設計語言的預設最低和最高工作階段數,以及預設的 gRPC 通道數。

C++

MinSessions: 100
MaxSessions: 400
NumChannels: 4

C#

MinSessions: 100
MaxSessions: 400
NumChannels: 4

Go

MinSessions: 100
MaxSessions: 400
NumChannels: 4

Java

MinSessions: 100
MaxSessions: 400
NumChannels: 4

Node.js

Node.js 用戶端不支援多個 gRPC 管道。因此建議您建立多個用戶端,而非將單一用戶端的會期集區大小增加到 100 個會期以上。

MinSessions: 25
MaxSessions: 100

PHP

PHP 用戶端不支援可設定的 gRPC 管道數量。

MinSessions: 1
MaxSessions: 500

Python

Python 支援四種不同的工作階段集區類型,可讓您用來管理工作階段。

Ruby

Ruby 用戶端不支援多個 gRPC 管道。因此建議您建立多個用戶端,而非將單一用戶端的會期集區大小增加到 100 個會期以上。

MinSessions: 10
MaxSessions: 100

應用程式使用的工作階段數,等於應用程式執行的並行交易數。只有在預期單一應用程式執行個體執行的並行交易數量,會超過預設工作階段集區可處理的數量時,才需要修改預設工作階段集區設定。

對於高並行應用程式,建議採取下列做法:

  1. MinSessions 設為單一用戶端將執行的預期並行交易數。
  2. MaxSessions 設為單一用戶端可執行的並行交易數量上限。
  3. 如果預期並行程度在應用程式生命週期內不會有太大變化,請設定 MinSessions=MaxSessions。這樣可防止工作階段集區擴大或縮小。擴大或縮減工作階段集區也會耗用部分資源。
  4. NumChannels 設為 MaxSessions / 100。一個 gRPC 管道最多可同時處理 100 個要求。如果發現尾端延遲時間 (p95/p99 延遲時間) 偏高,請增加這個值,因為這可能表示 gRPC 管道壅塞。

增加作用中的工作階段數量會使用 Spanner 資料庫服務和用戶端程式庫的額外資源。如果增加的會話數超過應用程式的實際需求,可能會降低系統效能。

增加工作階段集區,而非增加用戶端數量

應用程式的工作階段集區大小決定單一應用程式執行個體可執行的並行交易數量。不建議將工作階段集區大小調高至單一應用程式執行個體可處理的並行上限以上。如果應用程式收到大量要求,超過集區中的工作階段數,要求就會排入佇列,等待可用的工作階段。

用戶端程式庫會耗用下列資源:

  1. 每個 gRPC 通道都會使用一個 TCP 連線。
  2. 每次 gRPC 叫用都需要一個執行緒。用戶端程式庫使用的執行緒數量上限,等於應用程式執行的並行查詢數量上限。這些執行緒會優先於應用程式用於自身商業邏輯的任何執行緒。

不建議將工作階段集區的大小調高至超過單一應用程式執行個體可處理的執行緒數量上限。請改為增加應用程式執行個體數量。

管理寫入工作階段片段

針對部分用戶端程式庫,Spanner 會保留一部分的工作階段做為讀取/寫入交易之用,這稱之為寫入工作階段片段。當您的應用程式用盡所有讀取的工作階段,即使是唯讀交易,Spanner 仍會使用讀取/寫入的工作階段。讀取/寫入工作階段需要 spanner.databases.beginOrRollbackReadWriteTransaction。如果使用者為 spanner.databaseReader IAM 角色,則呼叫會失敗,而 Spanner 會傳回以下錯誤訊息:

generic::permission_denied: Resource %resource% is missing IAM permission:
spanner.databases.beginOrRollbackReadWriteTransaction

針對保有讀取/寫入工作階段片段的用戶端程式庫,即可以設定寫入工作階段的片段。

C++

所有 C++ 工作階段都是相同的。沒有讀取或僅限讀取/寫入的工作階段。

C#

C# 的預設寫入工作階段片段為 0.2。您可以使用 SessionPoolOptions 的 WriteSessionsFraction 欄位變更片段。

Go

所有 Go 工作階段都是相同的。沒有讀取或僅限讀取/寫入的工作階段。

Java

所有 Java 工作階段都是相同的。沒有讀取或僅限讀取/寫入的工作階段。

Node.js

所有 Node.js 工作階段都是相同的。沒有讀取或僅限讀取/寫入的工作階段。

PHP

所有 PHP 工作階段都是相同的。沒有讀取或僅限讀取/寫入的工作階段。

Python

Python 支援四種不同的工作階段集區類型,可讓您用來管理讀取與讀取/寫入工作階段。

Ruby

適用 Ruby 的預設寫入工作階段片段為 0.3。您可以使用用戶端初始化方法變更片段。

建立用戶端程式庫或使用 REST/RPC 時的最佳做法

下列各節將說明在 Spanner 用戶端程式庫實作工作階段,或是以 RESTRPC API 使用工作階段的最佳做法。

這些最佳做法僅適用於開發用戶端程式庫,或使用 REST/RPC API 的情況。如果您使用 Google Spanner 用戶端程式庫,請參閱使用 Google 用戶端程式庫的最佳做法

建立工作階段集區並調整大小

如要判斷用戶端程序中工作階段集區的最佳大小,請將預期並行的交易數量設定為最小值,然後將最大值設定為最初的測試數量,例如 100 如果最大的數量不合適,請再增加。增加作用中的工作階段數量會使用 Spanner 資料庫服務額外的資源,因此如果無法清除未使用的工作階段,效能會下降。針對 RPC API 的使用者,建議每個 gRPC 管道不要超過 100 個工作階段。

處理已刪除的工作階段

你可以透過下列三種方式刪除工作階段:

  • 用戶端可刪除工作階段。
  • 當工作階段閒置超過 1 小時,Spanner 資料庫服務可刪除工作階段。
  • 如果工作階段超過 28 天,Spanner 資料庫服務可能會刪除工作階段。

嘗試使用已刪除的工作階段會導致 NOT_FOUND。如果您遇到這個問題,請建立並使用新的工作階段,接著新增工作階段至集區,再將已刪除的工作階段從集區中移除。

保持閒置工作階段的運作

Spanner 資料庫服務會保留捨棄未使用工作階段的權利。如果您確定需要保持閒置工作階段的運作,例如預期資料庫的近期使用量會大增,即可避免捨棄閒置工作階段。請執行價格低廉的作業來讓工作階段保持運作,例如執行 SQL 查詢 SELECT 1。如果您有近期內不需使用的閒置工作階段,請允許 Spanner 捨棄該工作階段,然後下次需要時再建立一個新工作階段。

處理資料庫的一般尖峰需求也是保持工作階段運作的一種情境。如果每天從上午 9:00 到下午 6:00 都是資料庫重度使用的狀態,您必須在這個期間保持一些閒置工作階段的運作,因為在用量高峰時可能會需要這些工作階段。在下午 6:00 之後,Spanner 就可以捨棄這些閒置的工作階段。在每天上午 9:00 前,建立一些新的工作階段,為預期的需求做好準備。

另一個情境就是,您擁有使用 Spanner 的應用程式,但是必須避免其運作時的連線負擔。您也可以保持一組工作階段的運作,避免連線負擔。

針對用戶端程式庫的使用者隱藏工作階段詳細資料

如果您正在建立用戶端程式庫,請勿對用戶端程式庫的消費者公開工作階段。讓用戶端可以進行資料庫呼叫,而無需涉及建立與維護工作階段的複雜事務。如需針對用戶端程式庫消費者隱藏工作階段詳細資料的用戶端程式庫範例,請參閱 Java 適用的 Spanner 用戶端程式庫。

處理非冪等的寫入交易錯誤

無防重播功能的寫入交易可能會套用一次以上的異動。如果異動非冪等,套用一次以上的異動可能會導致執行失敗。舉例來說,即使嘗試寫入前並沒有任何資料列,插入作業仍可能因 ALREADY_EXISTS 而失敗。這會發生在後端伺服器修訂異動,但無法成功與用戶端進行通訊的情況。在這種事件中,系統會重新嘗試異動而導致 ALREADY_EXISTS 失敗。

當實作自有的用戶端資料庫或使用 REST API 時,請參考下列因應此情境的一些可能方法:

  • 建立寫入作業的結構使其成為冪等。
  • 使用具備防重播的寫入作業。
  • 實作可執行「更新+插入」邏輯的方法:如果是新資料則插入,如果是現有資料則更新。
  • 替用戶端處理錯誤。

維持穩定的連線

如要獲得最佳效能,用來託管工作階段的連線必須保持穩定。當託管工作階段的連線變更時,Spanner 可能會取消工作階段上運作的交易,在更新工作階段的中繼資料時,可能造成資料庫少量的額外負載。偶而發生的少數連線變更是沒有問題的,但仍須避免同時變更大量連線的狀況。如果在用戶端和 Spanner 之間使用 Proxy,則應維持每個工作階段的連線穩定性。

監控執行中的工作階段

您可以使用 ListSessions 指令,透過指令列REST APIRPC API 監控資料庫中的運作中工作階段。ListSessions 會顯示特定資料庫的有效工作階段。如果您需要找出工作階段洩漏的原因,這個方法就非常實用。(工作階段洩漏是一個意外事件,也就是已建立工作階段,但是並未回傳至工作階段集區以備重複使用)。

ListSessions 可讓您查看運行中工作階段相關的中繼資料,包含建立工作階段的時點與最後使用工作階段的時點。分析這份資料可引導正確進行工作階段疑難排解。如果多數運行中的工作階段都沒有近期的 approximate_last_use_time,表示應用程式並未正確地重複使用工作階段。如要進一步瞭解 approximate_last_use_time 欄位,請參閱 RPC API 參考資料

如要進一步瞭解如何使用 ListSessions,請參閱 REST API 參考資料RPC API 參考資料gcloud 指令列工具參考資料

自動清除工作階段洩漏問題

如果工作階段集區中的所有工作階段都已使用完畢,每項新交易都會等待,直到工作階段返回集區為止。如果建立工作階段,但未回傳至工作階段集區以供重複使用,這就是所謂的工作階段洩漏。如果發生工作階段洩漏,等待開啟工作階段的交易就會無限期停滯,並封鎖應用程式。工作階段洩漏通常是由長時間執行的問題交易造成,且這類交易並未提交。

您可以設定工作階段集區,自動解決這些閒置交易。啟用用戶端程式庫以自動解決非使用中轉換問題後,系統會找出可能導致工作階段洩漏的問題交易、從工作階段集區中移除,並以新的工作階段取代。

記錄也有助於找出這些有問題的交易。如果已啟用記錄功能,當工作階段集區的使用率超過 95% 時,系統預設會分享警告記錄。如果工作階段用量超過 95%,您可能需要增加工作階段集區允許的工作階段上限,或是可能發生工作階段洩漏。警告記錄包含執行時間超出預期的交易堆疊追蹤記錄,有助於找出工作階段集區使用率偏高的原因。系統會根據記錄匯出工具設定推送警告記錄。

啟用用戶端程式庫,自動解決非使用中交易

您可以啟用用戶端程式庫,傳送警告記錄並自動解決非使用中的交易,也可以啟用用戶端程式庫,只接收警告記錄。

Java

如要接收警告記錄並移除閒置交易,請使用 setWarnAndCloseIfInactiveTransactions

 final SessionPoolOptions sessionPoolOptions = SessionPoolOptions.newBuilder().setWarnAndCloseIfInactiveTransactions().build()

 final Spanner spanner =
         SpannerOptions.newBuilder()
             .setSessionPoolOption(sessionPoolOptions)
             .build()
             .getService();
 final DatabaseClient client = spanner.getDatabaseClient(databaseId);

如要只接收警告記錄,請使用 setWarnIfInactiveTransactions

 final SessionPoolOptions sessionPoolOptions = SessionPoolOptions.newBuilder().setWarnIfInactiveTransactions().build()

 final Spanner spanner =
         SpannerOptions.newBuilder()
             .setSessionPoolOption(sessionPoolOptions)
             .build()
             .getService();
 final DatabaseClient client = spanner.getDatabaseClient(databaseId);

Go

如要接收警告記錄並移除閒置交易,請搭配使用 SessionPoolConfigInactiveTransactionRemovalOptions

 client, err := spanner.NewClientWithConfig(
     ctx, database, spanner.ClientConfig{SessionPoolConfig: spanner.SessionPoolConfig{
         InactiveTransactionRemovalOptions: spanner.InactiveTransactionRemovalOptions{
         ActionOnInactiveTransaction: spanner.WarnAndClose,
         }
     }},
 )
 if err != nil {
     return err
 }
 defer client.Close()

如要只接收警告記錄,請使用 customLogger

 customLogger := log.New(os.Stdout, "spanner-client: ", log.Lshortfile)
 // Create a logger instance using the golang log package
 cfg := spanner.ClientConfig{
         Logger: customLogger,
     }
 client, err := spanner.NewClientWithConfig(ctx, db, cfg)

多工工作階段

多工工作階段可讓您在單一工作階段中建立大量並行要求。多工工作階段是您在多個 gRPC 管道中使用的 ID。不會造成任何額外的瓶頸。多工處理工作階段具有下列優點:

  • 由於工作階段管理通訊協定更簡單,後端資源耗用量也隨之減少。舉例來說,他們會避免與工作階段擁有權維護和垃圾收集相關的工作階段維護活動。
  • 長時間工作階段,閒置時不需要保持連線要求。

下列項目支援多工處理工作階段:

  • Java 和 Go 用戶端程式庫
  • 依附於 Java 和 Go 用戶端程式庫的 Spanner 生態系統工具,例如 PGAdapter、JDBC、Hibernate、資料庫/SQL 驅動程式和 GORM。

  • 依附於 Java 和 Go 用戶端程式庫的 Spanner 生態系統工具,例如 PGAdapter、JDBC、Hibernate、資料庫或 SQL 驅動程式,以及 GORM。您可以使用 OpenTelemetry 指標,查看現有工作階段集區和多工工作階段之間的流量分配情形。OpenTelemetry 具有指標篩選器 is_multiplexed,設為 true 時會顯示多工工作階段。

所有類型的交易都支援多工工作階段。

用戶端程式庫每 7 天會輪替多工工作階段,避免在過時的工作階段傳送交易。

多工工作階段預設為停用。您必須先使用環境變數啟用多工連線,才能在用戶端應用程式中使用這項功能。如要使用 Java 或 Go 啟用多工工作階段,請參閱「啟用多工工作階段」。

注意事項

如果您嘗試提交空白的讀取或寫入交易主體,或是每個查詢或 DML 陳述式都失敗的交易,則有幾種情況需要考量多工處理工作階段。多工工作階段需要您在每個提交要求中加入伺服器產生的預先提交權杖。如果交易包含查詢或 DML,伺服器必須先成功執行至少一筆查詢或 DML 交易,才能將有效權杖傳回用戶端程式庫。如果沒有任何查詢或 DML 交易成功,用戶端程式庫會在提交前隱含地新增 SELECT 1

如果多工連線上的讀取或寫入交易只有變動,且其中一個變動是針對結構定義中不存在的資料表或資料欄,用戶端可能會傳回 INVALID_ARGUMENT 錯誤,而非 NOT_FOUND 錯誤。

啟用多工工作階段

如要在用戶端應用程式中使用多工處理工作階段,您必須先設定環境變數來啟用這項功能。

如要啟用多工工作階段,請將 GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS 環境變數設為 TRUE。這個標記也會啟用 ReadOnly 交易的多工工作階段支援功能。

export GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS=TRUE

如要為多工工作階段啟用分割作業支援,請將 GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS_PARTITIONED_OPS 環境變數設為 TRUE

export GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS_PARTITIONED_OPS=TRUE

如要為多工工作階段啟用讀寫交易支援,請將 GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS_FOR_RW 環境變數設為 TRUE

export GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS_FOR_RW=True

如要在多工連線中支援交易,必須先將 GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS 設為 TRUE

查看一般和多工處理工作階段的流量

OpenTelemetry 具有 is_multiplexed 篩選器,可顯示多工工作階段的流量。將這個篩選器設為 true to view multiplexed sessions andfalse`,即可查看一般工作階段。

  1. 按照「Spanner OpenTelemetry」事前準備一節的程序,為 Spanner 設定 OpenTelemetry。
  2. 前往 Metrics Explorer

    前往 Metrics Explorer

  3. 在「指標」下拉式選單中,依 generic 篩選。

  4. 按一下「Generic Task」,然後依序前往「Spanner」 >「Spanner/num_acquired_sessions」

  5. 在「篩選器」欄位中,選取下列其中一個選項:

    a. is_multiplexed = false 即可查看一般課程。 b. is_multiplexed = true 即可查看多工工作階段。

    下圖顯示選取多工連線的「篩選」選項。

如要進一步瞭解如何搭配使用 OpenTelemetry 和 Spanner,請參閱「善用 OpenTelemetry 普及 Spanner 可觀測性」和「使用 OpenTelemetry 檢查 Spanner 元件的延遲時間」。

Opentelemetry 資訊主頁顯示 is-multiplexed 篩選器。

疑難排解

應用程式可能會遇到的常見工作階段相關錯誤包括:

  • Session not found
  • RESOURCE_EXHAUSTED

詳情請參閱「工作階段錯誤」。