首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> SQL Server 資料庫 >> 關於SqlServer

SQL Server Timestamp (Rowversion) 資料類型深度解析:版本控管、並發檢查與 BigInt 轉換實戰

     編輯:關於SqlServer

核心概念澄清

  • Timestamp ≠ 時間/日期:它是 資料庫級別的遞增二進位計數器 (自 SQL Server 2005 起更名為 ROWVERSION)
  • 每次 INSERT/UPDATE 操作該資料列時,自動更新為當前 @@DBTS + 1
  • 唯一性:資料庫範圍內唯一,可作為資料列版本標識
  • 儲存大小:固定 8 位元組 (VARBINARY(8))
  • 不可手動更新/插入指定值 (僅能插入 NULL 觸發自動產生)

建立與基本操作

-- 建表:timestamp 欄位無需指定名稱 (系統自動命名為 timestamp/rowversion)
CREATE TABLE TestTable (
    ID INT PRIMARY KEY,
    DataCol VARCHAR(100),
    Version ROWVERSION  -- 或 TIMESTAMP,同義
);

-- 插入:Version 欄位給 NULL 或省略
INSERT INTO TestTable (ID, DataCol) VALUES (1, 'Initial');
INSERT INTO TestTable (ID, DataCol) VALUES (2, 'Second');

-- 查看:自動產生的二進位值
SELECT ID, DataCol, Version FROM TestTable;
-- 結果範例:
-- 1 | Initial | 0x00000000000007D0
-- 2 | Second  | 0x00000000000007D1

-- 更新:Version 自動變更
UPDATE TestTable SET DataCol = 'Updated' WHERE ID = 1;
SELECT Version FROM TestTable WHERE ID = 1;  -- 0x00000000000007D2 (新值)

@@DBTS:資料庫當前 Timestamp 值

SELECT @@DBTS;  -- 回傳 VARBINARY(8),如 0x00000000000007D2
-- 等同於:
SELECT CONVERT(VARBINARY(8), @@DBTS);
  • 資料庫啟動後單調遞增,重啟不重置 (除非還原備份)
  • 可用於追蹤資料庫整體變更進度

Timestamp 與 BigInt 互相轉換

轉換原理

TIMESTAMP/ROWVERSION 本質是 8 位元組無號整數,可安全轉換為 BIGINT (有號 8 位元組),數值相等。

轉換範例

-- Timestamp → BigInt
SELECT 
    Version,
    CONVERT(BIGINT, Version) AS Version_BigInt,
    CONVERT(VARBINARY(8), CONVERT(BIGINT, Version)) AS BackToVarBinary
FROM TestTable;

-- BigInt → Timestamp (比較/查詢用)
DECLARE @ts BIGINT = 2000;  -- 對應 0x00000000000007D0
SELECT * FROM TestTable WHERE Version = CONVERT(VARBINARY(8), @ts);

-- 使用 @@DBTS 追蹤變更
DECLARE @last_check BIGINT = CONVERT(BIGINT, @@DBTS);
-- ... 一段時間後 ...
SELECT * FROM TestTable 
WHERE CONVERT(BIGINT, Version) > @last_check;  -- 找出此期間被修改的列

實戰應用場景

1. 樂觀並發控制 (Optimistic Concurrency)

-- 應用讀取時取得 Version
SELECT ID, DataCol, Version FROM TestTable WHERE ID = 1;
-- 傳回 Version = 0x00000000000007D2

-- 更新時攜帶原始 Version 做 WHERE 條件
UPDATE TestTable 
SET DataCol = 'New Value', 
    Version = Version  -- 觸發自動更新,但這行其實多餘,WHERE 已足夠
WHERE ID = 1 AND Version = 0x00000000000007D2;

-- 檢查 @@ROWCOUNT
IF @@ROWCOUNT = 0
    RAISERROR('資料已被其他使用者修改,請重新讀取', 16, 1);

優點:無鎖定、高並發、資料庫原生支援。

2. 變更追蹤 / 同步機制

-- 記錄上次同步的 Timestamp
CREATE TABLE SyncWatermark (TableName SYSNAME PRIMARY KEY, LastVersion BIGINT);
INSERT INTO SyncWatermark VALUES ('TestTable', CONVERT(BIGINT, @@DBTS));

-- 定期同步:只抓取變更列
DECLARE @last BIGINT = (SELECT LastVersion FROM SyncWatermark WHERE TableName = 'TestTable');
SELECT ID, DataCol, Version 
FROM TestTable 
WHERE CONVERT(BIGINT, Version) > @last;

-- 更新水位線
UPDATE SyncWatermark SET LastVersion = CONVERT(BIGINT, @@DBTS) WHERE TableName = 'TestTable';

3. 快照隔離 / 版本向量

-- 結合多表的版本向量
SELECT 
    'Orders' AS TableName, MAX(CONVERT(BIGINT, Version)) AS MaxVersion FROM Orders
UNION ALL
SELECT 'OrderDetails', MAX(CONVERT(BIGINT, Version)) FROM OrderDetails
UNION ALL
SELECT 'Customers', MAX(CONVERT(BIGINT, Version)) FROM Customers;

效能與儲存考量

  • 索引:Version 欄位極適合建立非叢集索引 (高選擇性、單調遞增),支援範圍查詢
  • 填充因子:單調遞增特性意味著新值總在索引尾部,FILLFACTOR = 100 最佳
  • 儲存開銷:每列固定 +8 位元組,窄表比例較高,寬表可忽略
  • 複寫/CDC:合併式複寫、Change Data Capture 內部大量使用 Rowversion

常見誤區與避坑指南

誤區正確認知
以為包含日期時間資訊純遞增數字,無時間語義,無法轉換為 DATETIME
嘗試手動設定值INSERT/UPDATE 時給定值會報錯,只能給 NULL
以為表級別唯一資料庫級別唯一,跨表可比較大小
重啟資料庫會歸零不歸零,僅還原備份/附加資料庫可能改變
轉 BIGINT 會溢位ROWVERSION 最大 2^64-1,BIGINT 最大 2^63-1,極極極罕見才溢位 (需 1.8×10^19 次更新),實務可忽略
用於分頁/排序替代 ID可用但不推薦:ID 語意清晰、可控;Version 為系統維護、間距不固定

進階:記憶體優化表中的 Rowversion

  • 記憶體優化表 (In-Memory OLTP) 不支援 ROWVERSION/TIMESTAMP
  • 替代方案:應用層維護版本欄位 (BIGINT + INTERLOCKED.INCREMENT)、或使用 SYSTEM_VERSIONING (Temporal Tables)

完整測試腳本 (可直接執行驗證)

SET NOCOUNT ON;

-- 清理
IF OBJECT_ID('dbo.TimestampDemo') IS NOT NULL DROP TABLE dbo.TimestampDemo;

-- 建表
CREATE TABLE dbo.TimestampDemo (
    ID INT IDENTITY PRIMARY KEY,
    Info VARCHAR(50),
    Ver ROWVERSION
);

-- 插入 3 列
INSERT INTO dbo.TimestampDemo (Info) VALUES ('Row 1'), ('Row 2'), ('Row 3');

-- 記錄初始版本
DECLARE @v1 VARBINARY(8), @v2 VARBINARY(8), @v3 VARBINARY(8);
SELECT @v1 = Ver FROM dbo.TimestampDemo WHERE ID = 1;
SELECT @v2 = Ver FROM dbo.TimestampDemo WHERE ID = 2;
SELECT @v3 = Ver FROM dbo.TimestampDemo WHERE ID = 3;

PRINT '初始版本: ID1=' + CONVERT(VARCHAR(20), @v1, 1) + ', ID2=' + CONVERT(VARCHAR(20), @v2, 1) + ', ID3=' + CONVERT(VARCHAR(20), @v3, 1);
PRINT 'BigInt: ID1=' + CAST(CONVERT(BIGINT, @v1) AS VARCHAR(20)) + ', ID2=' + CAST(CONVERT(BIGINT, @v2) AS VARCHAR(20)) + ', ID3=' + CAST(CONVERT(BIGINT, @v3) AS VARCHAR(20));

-- 更新 ID=2
UPDATE dbo.TimestampDemo SET Info = 'Row 2 Updated' WHERE ID = 2;

-- 查詢更新後版本
DECLARE @v2_new VARBINARY(8);
SELECT @v2_new = Ver FROM dbo.TimestampDemo WHERE ID = 2;
PRINT '更新後 ID2 版本: ' + CONVERT(VARCHAR(20), @v2_new, 1) + ' (BigInt: ' + CAST(CONVERT(BIGINT, @v2_new) AS VARCHAR(20)) + ')';

-- 驗證其他列版本未變
DECLARE @v1_chk VARBINARY(8), @v3_chk VARBINARY(8);
SELECT @v1_chk = Ver FROM dbo.TimestampDemo WHERE ID = 1;
SELECT @v3_chk = Ver FROM dbo.TimestampDemo WHERE ID = 3;
PRINT 'ID1 未變: ' + CASE WHEN @v1_chk = @v1 THEN 'True' ELSE 'False' END;
PRINT 'ID3 未變: ' + CASE WHEN @v3_chk = @v3 THEN 'True' ELSE 'False' END;

-- 使用 BigInt 範圍查詢找出變更列
DECLARE @threshold BIGINT = CONVERT(BIGINT, @v2);  -- 更新前 ID2 的版本
SELECT ID, Info, Ver, CONVERT(BIGINT, Ver) AS Ver_BigInt
FROM dbo.TimestampDemo
WHERE CONVERT(BIGINT, Ver) > @threshold;

-- 清理
DROP TABLE dbo.TimestampDemo;
PRINT '測試完成。';

總結

ROWVERSION/TIMESTAMP 是 SQL Server 實現無鎖樂觀並發、高效變更追蹤的核心工具。關鍵要點:

  1. 本質是資料庫級單調遞增 8 位元組計數器
  2. 與 BIGINT 雙向無損轉換,便於應用層比較/儲存
  3. 配合 @@DBTS 實現資料庫/表級變更水位線追蹤
  4. 索引效能極佳,適合大表變更篩選
  5. 不可手動賦值、不含時間語義、記憶體優化表不支援

善用此類型,能大幅簡化並發控制邏輯,避免悲觀鎖定帶來的封鎖與死結風險。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved