更新結構定義

Spanner 可讓您在不停機的情況下更新結構定義。您可透過下列幾種方式更新現有資料庫的結構定義:

支援的結構定義更新

Spanner 支援下列現有資料庫的結構定義更新:

  • 新增或捨棄具名結構定義。
  • 建立新資料表。新資料表中的資料欄可以是 NOT NULL
  • 刪除資料表,前提是資料表中沒有其他交錯的資料表,而且沒有次要索引。
  • 建立或刪除含有外鍵的資料表。
  • 在現有資料表中新增或移除外鍵。
  • 新增非鍵資料欄到任何資料表。新的非鍵資料欄不得為 NOT NULL
    • 從任何資料表中捨棄非鍵資料欄,除非有次要索引、外鍵、儲存產生的資料欄或檢查限制使用該非鍵資料欄。
  • NOT NULL 新增至非鍵資料欄,但不包括 ARRAY 欄。
  • 從非鍵資料欄移除 NOT NULL
  • STRING 資料欄變更為 BYTES 資料欄,或將 BYTES 資料欄變更為 STRING 資料欄。
  • PROTO 資料欄變更為 BYTES 資料欄,或將 BYTES 資料欄變更為 PROTO 資料欄。
  • 變更 PROTO 資料欄的 Proto 訊息類型。
  • 使用 ALTER PROTO BUNDLE 將新值新增至 ENUM 定義,並重新命名現有值。
  • 以任意方式變更 PROTO BUNDLE 中定義的訊息,但前提是這些訊息的修改欄位不得做為任何資料表中的鍵,且現有資料符合新限制。
  • 增加或減少 STRINGBYTES 類型的長度限制 (包括 MAX),除非資料欄是主鍵資料欄,且有一個或多個子資料表。
  • 增加或減少 ARRAY<STRING>ARRAY<BYTES>ARRAY<PROTO> 資料欄的長度限制,最多可達允許上限。
  • 啟用或停用值和主鍵資料欄中的修訂時間戳記
  • 新增或移除次要索引。
  • 在現有資料表中新增或移除檢查條件約束。
  • 從現有資料表新增或移除儲存的產生資料欄。
  • 建構新的最佳化工具統計資料套件
  • 建立及管理檢視畫面
  • 建立及管理序列
  • 建立資料庫角色並授予權限。
  • 設定、變更或捨棄資料欄的預設值。
  • 變更資料庫選項 (例如 default_leaderversion_retention_period)。
  • 建立及管理變更串流
  • 建立及管理機器學習模型。

不支援的結構定義更新

Spanner 不支援下列現有資料庫的結構定義更新:

  • 如果資料表或索引鍵參照 ENUM 類型的 PROTO 欄位,您就無法從 proto 列舉中移除 ENUM 值。(系統支援從 ENUM<> 資料欄使用的列舉中移除 ENUM 值,包括這些資料欄做為鍵時。)

  • STRING(36) 資料欄變更為 UUID 資料欄,或將 UUID 資料欄變更為 STRING(36) 資料欄。

結構定義更新效能

在 Spanner 中更新結構定義不需要停機。當您向 Spanner 資料庫發出一批 DDL 陳述式時,您可以在 Spanner 透過長時間執行的作業套用更新時,繼續不間斷地從資料庫進行寫入和讀取作業。

執行 DDL 陳述式所需的時間取決於更新是否需要驗證現有的資料,或補充任何資料。舉例來說,如果您將 NOT NULL 註解新增至現有的資料欄,Spanner 必須讀取資料欄中的所有值,以確認資料欄不包含任何 NULL 值。如果有很多資料需要驗證,這個步驟可能需要花費較長的時間。另一個例子是,如果您將索引新增至資料庫:Spanner 會使用現有的資料來補充索引。根據索引的定義和對應的主資料表的大小,這個程序可能需要較長的時間。不過,如果您是新增資料欄到資料表,就沒有現有的資料可以驗證,因此 Spanner 能更迅速地進行更新。

簡單來說,不需要 Spanner 驗證現有資料的結構定義更新只需要幾分鐘。需要進行驗證的結構定義更新則需要較長的時間,時間長短取決於需要驗證的現有資料量。資料驗證能以較實際工作環境流量低的優先順序在背景中執行。下節將詳細探討需要進行資料驗證的結構定義更新。

根據檢視定義驗證結構定義更新

更新結構定義時,Spanner 會驗證更新不會導致用來定義現有檢視區塊的查詢失效。如果驗證成功,結構定義更新就會成功。如果驗證失敗,結構定義更新就會失敗。詳情請參閱建立檢視區塊的最佳做法

需要驗證資料的結構定義更新

您可以將結構定義更新設定為需要驗證現有資料符合新限制後才能完成。當結構定義更新需要驗證資料時,Spanner 不允許在受影響的結構定義有相衝突的結構定義更新,並且會在背景中驗證資料。如果驗證成功,結構定義更新就會成功。如果驗證失敗,結構定義更新就會失敗。驗證作業是透過長時間執行的作業執行。您可以檢查這些作業的狀態,以判定作業成功或失敗。

舉例來說,假設您已定義下列 music.proto 檔案,其中包含 RecordLabel 列舉和 Songwriter 通訊協定訊息:

  enum RecordLabel {
    COOL_MUSIC_INC = 0;
    PACIFIC_ENTERTAINMENT = 1;
    XYZ_RECORDS = 2;
  }

  message Songwriter {
    required string nationality   = 1;
    optional int64  year_of_birth = 2;
  }

如要在結構定義中新增 Songwriters 資料表,請按照下列步驟操作:

GoogleSQL

CREATE PROTO BUNDLE (
  googlesql.example.music.Songwriter,
  googlesql.example.music.RecordLabel,
);

CREATE TABLE Songwriters (
  Id         INT64 NOT NULL,
  FirstName  STRING(1024),
  LastName   STRING(1024),
  Nickname   STRING(MAX),
  OpaqueData BYTES(MAX),
  SongWriter googlesql.example.music.Songwriter
) PRIMARY KEY (Id);

CREATE TABLE Albums (
  SongwriterId     INT64 NOT NULL,
  AlbumId          INT64 NOT NULL,
  AlbumTitle       STRING(MAX),
  Label            INT32
) PRIMARY KEY (SongwriterId, AlbumId);

系統會允許下列結構定義更新,但這些更新需要進行驗證。根據現有的資料量,可能需要較長的時間才能完成驗證:

  • NOT NULL 註解新增至非鍵資料欄。例如:

    ALTER TABLE Songwriters ALTER COLUMN Nickname STRING(MAX) NOT NULL;
    
  • 縮減資料欄的長度。例如:

    ALTER TABLE Songwriters ALTER COLUMN FirstName STRING(10);
    
  • BYTES 變更為 STRING。例如:

    ALTER TABLE Songwriters ALTER COLUMN OpaqueData STRING(MAX);
    
  • INT64/INT32 變更為 ENUM。例如:

    ALTER TABLE Albums ALTER COLUMN Label googlesql.example.music.RecordLabel;
    
  • RecordLabel 列舉定義中移除現有值。

  • 在現有的 TIMESTAMP 資料欄上啟用修訂時間戳記。例如:

    ALTER TABLE Albums ALTER COLUMN LastUpdateTime SET OPTIONS (allow_commit_timestamp = true);
    
  • 在現有資料表中加入檢查條件約束。

  • 在現有資料表中新增儲存的產生資料欄。

  • 建立含有外鍵的新資料表。

  • 在現有資料表中加入外鍵。

如果基礎資料不符合新限制,這些結構定義更新就會失敗。舉例來說,如果 Nickname 資料欄中的任何值為 NULLALTER TABLE Songwriters ALTER COLUMN Nickname STRING(MAX) NOT NULL 陳述式會失敗,因為現有資料不符合新定義的 NOT NULL 限制。

資料驗證可能需要幾分鐘到幾小時的時間。完成資料驗證的時間取決於下列因素:

  • 資料集大小
  • 執行個體的運算能力
  • 執行個體的負載

結構定義更新完成之前,有些結構定義更新可能會變更傳送要求到資料庫的行為。舉例來說,如果您將 NOT NULL 新增到資料欄,Spanner 會立即開始拒絕在資料欄寫入 NULL 的新要求。如果新結構定義更新最終因資料驗證而失敗,則會有一段封鎖寫入作業的時間,即使舊結構定義可接受這種寫入也是如此。

您可以使用 projects.instances.databases.operations.cancel 方法或 gcloud spanner operations,取消長時間執行的資料驗證作業。

在結構定義更新期間建立的結構定義版本

Spanner 使用結構定義版本管理,因此對大型資料庫進行結構定義更新期間無須停機。Spanner 會維護較舊的結構定義版本,以在處理結構定義更新時支援讀取作業。接著,Spanner 會建立一或多個新的結構定義版本,以處理結構定義更新。每個版本都會在單一不可分割的變更中包含陳述式集合的結果。

結構定義版本未必會以一對一的方式對應到其中一批 DDL 陳述式或個別 DDL 陳述式。某些個別 DDL 陳述式 (例如現有基本資料表的索引建立,或需要資料驗證的陳述式) 會導致多個結構定義版本。在其他情況下,可在單一版本中將多個 DDL 陳述式組成批次。舊版結構定義會耗用大量伺服器和儲存空間資源,且會保留至過期為止 (不再需要用於提供舊版資料的讀取作業)。

下表顯示 Spanner 更新結構定義所需的時間。

結構定義作業 預估持續時間
CREATE TABLE 分鐘
CREATE INDEX

數分鐘到數小時,前提是索引是在主資料表建立後才建立。

數分鐘,前提是陳述式與主資料表的 CREATE TABLE 陳述式同時執行。

DROP TABLE 分鐘
DROP INDEX 數分鐘
ALTER TABLE ... ADD COLUMN 分鐘
ALTER TABLE ... ALTER COLUMN

數分鐘到數小時,前提是需要背景驗證

數分鐘,前提是不需要背景驗證。

ALTER TABLE ... DROP COLUMN 分鐘
ANALYZE

視資料庫大小而定,可能需要幾分鐘到幾小時。

資料類型變更和變更串流

如果您變更變更串流監看的資料欄資料類型,後續相關變更串流記錄column_types 欄位會反映新類型,記錄 mods 欄位中的 old_values JSON 資料也會反映新類型。

變更串流記錄 mods 欄位的 new_values 一律與資料欄的目前類型相符。變更監控資料欄的資料類型,不會影響該變更之前的任何變更串流記錄。

BYTES 變更為 STRING 的特定情況下,Spanner 會在更新結構定義時驗證資料欄的舊值。因此,在寫入任何後續變更串流記錄時,Spanner 已安全地將舊的 BYTES 型別值解碼為字串。