本主題說明如何為每個使用 Spanner 執行的插入和更新作業,寫入認可時間戳記。如要使用這項功能,請在 TIMESTAMP
資料欄上設定 allow_commit_timestamp
選項,然後將時間戳記寫入為每個交易的一部分。
總覽
依據 TrueTime 技術,此修訂時間戳記為交易修訂到資料庫的時間。allow_commit_timestamp
資料欄選項可讓您以不可分割的形式,將修訂時間戳記儲存到資料欄。使用儲存於資料表的修訂時間戳記,可判斷變異的確切順序,並建構類似變更記錄的功能。
如要在資料庫中插入修訂時間戳記,請完成下列步驟:
在結構定義中,建立類型為
TIMESTAMP
的資料欄,並將資料欄選項allow_commit_timestamp
設為true
。例如:CREATE TABLE Performances ( ... LastUpdateTime TIMESTAMP NOT NULL OPTIONS (allow_commit_timestamp=true) ... ) PRIMARY KEY (...);
如果您要使用 DML 執行插入或更新作業,請使用
PENDING_COMMIT_TIMESTAMP
函式寫入修訂時間戳記。如果您要使用變異來執行插入或更新作業,請在修訂時間戳記欄的插入或更新作業中使用預留位置字串
spanner.commit_timestamp()
。您也可以使用用戶端程式庫提供的提交時間戳記常數。舉例來說,Java 用戶端中的這個常數為Value.COMMIT_TIMESTAMP
。
當 Spanner 使用這些預留位置做為資料欄值來修訂交易時,實際的修訂時間戳記會寫入指定的資料欄 (例如 LastUpdateTime
資料欄)。接著,您可以使用這個欄位值,在資料表建立更新的記錄。
修訂時間戳記值不保證不會重複。寫入非重疊欄位組的交易可能會有相同的時間戳記,而寫入重疊欄位組的交易則會有不重複的時間戳記。
Spanner 修訂時間戳記精細程度為毫秒等級,若儲存於 TIMESTAMP
資料欄則會轉換為奈秒。
建立及刪除修訂時間戳記欄
使用 allow_commit_timestamp
欄位選項,新增及移除對「修訂時間戳記」的支援:
- 建立新資料表時,指定資料欄支援修訂時間戳記。
- 變更現有資料表時:
- 新增資料欄以支援修訂時間戳記。
- 變更現有的
TIMESTAMP
欄位,使其支援修訂時間戳記。 - 變更現有
TIMESTAMP
欄,移除修訂時間戳記支援
索引鍵與索引
您可以使用修訂時間戳記欄做為主鍵欄或非主鍵欄。主鍵可以定義為 ASC
或 DESC
。
ASC
(預設) - 遞增的索引鍵適用於回應之前特定時間的查詢。DESC
- 遞減的索引鍵可以讓最後一個資料列留在資料表的頂端,讓您快速存取最新的資料。
父項資料表和子資料表的主鍵 allow_commit_timestamp
選項必須一致。如果主鍵的這個選項不一致,Spanner 會傳回錯誤。只有在您建立或更新結構定義時,才能有不一致的選項。
在下列情況下使用修訂時間戳記會產生熱點,進而降低資料效能:
將修訂時間戳記欄做為資料表主鍵的第一部分:
CREATE TABLE Users ( LastAccess TIMESTAMP NOT NULL, UserId INT64 NOT NULL, ... ) PRIMARY KEY (LastAccess, UserId);
次要索引主鍵的第一部分:
CREATE INDEX UsersByLastAccess ON Users(LastAccess)
或
CREATE INDEX UsersByLastAccessAndName ON Users(LastAccess, FirstName)
即使寫入率偏低,熱點也會降低資料效能。如果在未編入索引的非索引鍵資料欄上啟用修訂時間戳記,不會造成效能負擔。
建立修訂時間戳記欄
下列 DDL 會建立一個資料表,其中包含支援修訂時間戳記的資料欄。
CREATE TABLE Performances (
SingerId INT64 NOT NULL,
VenueId INT64 NOT NULL,
EventDate Date,
Revenue INT64,
LastUpdateTime TIMESTAMP NOT NULL OPTIONS (allow_commit_timestamp=true)
) PRIMARY KEY (SingerId, VenueId, EventDate),
INTERLEAVE IN PARENT Singers ON DELETE CASCADE
新增選項將變更時間戳記欄,如下:
- 您可以使用
spanner.commit_timestamp()
預留位置字串 (或用戶端程式庫提供的常數) 進行插入或更新作業。 - 資料欄只能包含過去的值。如需詳細資訊,請參閱自行提供時間戳記的值。
allow_commit_timestamp
選項會區分大小寫。
在現有資料表中新增修訂時間戳記欄
如要新增修訂時間戳記欄到現有資料表,請使用 ALTER TABLE
陳述式。舉例來說,如要在 Performances
資料表中新增 LastUpdateTime
欄,請使用下列陳述式:
ALTER TABLE Performances ADD COLUMN LastUpdateTime TIMESTAMP
NOT NULL OPTIONS (allow_commit_timestamp=true)
將時間戳記欄轉換為修訂時間戳記欄
您也可以將現有的時間戳記欄轉換成修訂時間戳記欄,不過這麼做需要 Spanner 驗證現有的時間戳記值是在過去。例如:
ALTER TABLE Performances ALTER COLUMN LastUpdateTime
SET OPTIONS (allow_commit_timestamp=true)
您無法在包含 SET OPTIONS
的 ALTER TABLE
陳述式中,變更資料欄的資料類型或 NULL
註記。詳情請參閱「資料定義語言」。
移除修訂時間戳記選項
如要從資料欄移除修訂時間戳記支援,請在 ALTER TABLE
陳述式中使用 allow_commit_timestamp=null
選項。即使移除了修訂時間戳記行為,該資料欄仍是時間戳記。變更選項並不會改變資料欄的其他特性,例如類型或空值 (NOT NULL
)。例如:
ALTER TABLE Performances ALTER COLUMN LastUpdateTime
SET OPTIONS (allow_commit_timestamp=null)
使用 DML 陳述式寫入修訂時間戳記
您可以使用 PENDING_COMMIT_TIMESTAMP
函式,在 DML 陳述式中寫入修訂時間戳記。Spanner 會在交易進行修訂時選擇修訂時間戳記。
以下 DML 陳述式會以修訂時間戳記更新 Performances
資料表中的 LastUpdateTime
欄:
UPDATE Performances SET LastUpdateTime = PENDING_COMMIT_TIMESTAMP()
WHERE SingerId=1 AND VenueId=2 AND EventDate="2015-10-21"
以下程式碼範例會使用 PENDING_COMMIT_TIMESTAMP
函式,在 LastUpdateTime
資料欄中寫入修訂時間戳記。
C++
C#
Go
Java
Node.js
PHP
Python
Ruby
Ruby
修訂時間戳記只能寫入以 allow_commit_timestamp=true
選項註記的資料欄。
若多個資料表中的資料列包含變異,您必須在每個資料表的修訂時間戳記欄指定 spanner.commit_timestamp()
(或用戶端程式庫常數)。
查詢修訂時間戳記欄
以下範例會查詢資料表中的修訂時間戳記欄。
C++
C#
Go
Java
Node.js
PHP
Python
Ruby
自行提供時間戳記欄的值
您可以自行提供修訂時間戳記欄的值,取代傳送 spanner.commit_timestamp()
(或用戶端程式庫常數) 做為資料欄值。這個值必須是過去的時間戳記。此限制可確保寫入時間戳記的費用低廉且作業迅速。若您指定的是未來時間戳記,伺服器會傳回 FailedPrecondition
錯誤。
建立變更記錄
假設您想要為資料表中發生的每個變異建立變更記錄,然後使用該變更記錄進行稽核。例如將變更記錄儲存到文書處理文件的資料表,由於時間戳記可強制排序變更記錄項目,修正時間戳記能讓建立變更記錄更加容易。您可以建構一個變更記錄,使用結構定義將變更歷程儲存到指定文件,如下方範例所示:
CREATE TABLE Documents (
UserId INT64 NOT NULL,
DocumentId INT64 NOT NULL,
Contents STRING(MAX) NOT NULL,
) PRIMARY KEY (UserId, DocumentId);
CREATE TABLE DocumentHistory (
UserId INT64 NOT NULL,
DocumentId INT64 NOT NULL,
Ts TIMESTAMP NOT NULL OPTIONS (allow_commit_timestamp=true),
Delta STRING(MAX),
) PRIMARY KEY (UserId, DocumentId, Ts),
INTERLEAVE IN PARENT Documents ON DELETE NO ACTION;
如要建立變更記錄,在插入或更新 Document
中資料列的交易中,於 DocumentHistory
中插入新資料列。在 DocumentHistory
中插入新資料列時,使用預留位置 spanner.commit_timestamp()
(或用戶端程式庫常數),告訴 Spanner 將認可時間戳記寫入資料欄 Ts
。將 DocumentsHistory
資料表與 Documents
資料表交錯將允許在地運算並使插入與更新作業更有效率,但也增加了限制,上層與下層資料列必須同時刪除。若要在刪除 Documents
中的資料列之後仍保留 DocumentHistory
中的資料列,請勿交錯這些資料表。
使用修訂時間戳記最佳化近期資料查詢
提交時間戳記可啟用 Spanner 最佳化功能,在擷取特定時間後寫入的資料時,可減少查詢 I/O。
如要啟用這項最佳化功能,查詢的 WHERE
子句必須比較資料表的提交時間戳記資料欄與您提供的特定時間,並使用下列屬性:
提供特定時間做為常數運算式:常值、參數或函式,其中函式本身的引數會評估為常數。
透過
>
或>=
運算子,比較修訂時間戳記是否比指定時間更晚。您也可以使用
AND
為WHERE
子句加入更多限制。使用OR
擴充子句會使查詢無法進行這項最佳化。
舉例來說,請考慮下列包含修訂時間戳記欄的 Performances
資料表:
CREATE TABLE Performances (
SingerId INT64 NOT NULL,
VenueId INT64 NOT NULL,
EventDate DATE,
Revenue INT64,
LastUpdateTime TIMESTAMP NOT NULL OPTIONS (allow_commit_timestamp=true)
) PRIMARY KEY (SingerId, VenueId, EventDate);
這項查詢可從前述的提交時間戳記最佳化方式中受益,因為它在資料表的提交時間戳記欄與常數運算式 (在本例中為文字常值) 之間進行大於或���於的���較:
SELECT * FROM Performances WHERE LastUpdateTime >= "2022-05-01";
下列查詢也符合最佳化條件,因為它在提交時間戳記和函式之間進行大於比較,且該函式在查詢執行期間的所有引數都會評估為常數:
SELECT * FROM Performances
WHERE LastUpdateTime > TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 30 DAY);
後續步驟
- 使用修訂時間戳記,利用 Go 建立變更記錄。