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