timestamp 這種數據類型表現自動生成的二進制數,確保這些數在數據庫中是唯一的。timestamp 一般用作給表行加版本戳的機制。存儲大小為 8 字節。
一個表只能有一個 timestamp 列。每次插入或更新包含 timestamp 列的行時,timestamp 列中的值均會更新。這一屬性使 timestamp 列不適合作為鍵使用,尤其是不能作為主鍵使用。對行的任何更新都會更改 timestamp 值,從而更改鍵值。如果該列屬於主鍵,那么舊的鍵值將無效,進而引用該舊值的外鍵也將不再有效。如果該表在動態游標中引用,則所有更新均會更改游標中行的位置。如果該列屬於索引鍵,則對數據行的所有更新還將導致索引更新。
不可為空的 timestamp 列在語義上等價於 binary(8) 列。可為空的 timestamp 列在語義上等價於 varbinary(8) 列。
在實際的多用戶並發訪問的生產環境里邊,我們經常要盡可能的保持數據的一致性。而其中最典型的例子就是我們從表里邊讀取數據,檢查驗證后對數據進行修改,然后寫回到數據庫中。在讀取和寫入的過程中,如果在多用戶並發的環境里邊,其他用戶已經把你要修改的數據
進行了修改是非常有可能發生的情況,這樣就造成了數據的不一致性。解決這樣的辦法,SQL SERVER提出了樂觀鎖定和悲觀鎖定的概念,下邊我以一個實例來說明如何使用樂觀鎖定和悲觀鎖定來解決這樣的問題。
/* 建立測試表:Train_ticket,代表一個真實的火車票庫,供用戶注冊.用戶要從里邊購買一個未使用的火車票,也就是S_Flag=0的票,給用戶注冊:更新T_Name,T_Time,S_Flag字段. 如果出現兩個用戶同時更新一張票的情況,是不能容忍的,也就是我們所說的數據不一致行。*/
create table Train_ticket(T_NO varchar(20),T_Name varchar(20),S_Flag bit,T_Time datetime)
悲觀鎖定解決方案
Begin Tran
select top 1 @TrainNo=T_NO
from Train_ticket with (UPDLOCK) where S_Flag=0
update Train_ticket
set T_Name=user,
T_Time=getdate(),
S_Flag=1
where
commit
注意其中的區別了嗎?with(updlock),是的,我們在查詢的時候使用了with (UPDLOCK)選項,在查詢記錄的時候我們就對記錄加上了更新鎖,表示我們即將對次記錄進行更新.注意更新鎖和共享鎖是不沖突的,也就是其他用戶還可以查詢此表的內容,但是和更新鎖和排它鎖是沖突的.所以其他的更新用戶就會阻塞.如果我們在另外一個窗口執行此代碼,同樣不加waifor delay子句.兩邊執行完畢后,我們發現成功的注冊了兩張火車票.可能我們已經發現了悲觀鎖定的缺點:當一個用戶進行更新的事務的時候,其他更新用戶必須排隊等待,即使那個用戶更新的不是同一條記錄.
樂觀鎖定解決方案
-- 首先我們在Train_ticket表里邊加上一列T_TimeStamp 列,該列是varbinary(8)類型.但是在更新的時候這個值會自動增長.
alter table Train_ticket add T_TimeStamp timestamp not null
-- 取得號和原始的時間戳值
select top 1 @TrainNo=T_No,
@timestamp=T_TimeStamp
from Train_ticket
where S_Flag=0
-- 延遲50秒,模擬並發訪問.
waitfor delay '000:00:50'
-- 購買票,但是要比較時間戳是否發生了變化.如果沒有發生變化.更新成功.如果發生變化,更新失敗.
update Train_ticket
set T_Name=user,
T_Time=getdate(),
S_Flag=1
where and
set @rowcount=@@rowcount
if @rowcount=1
begin
print 'Successful!'
commit
end
else if @rowcount=0
begin
if exists(select 1 from Train_ticket where )
begin
print 'The ticket was already buyed.'
rollback tran
end
else
begin
print 'This ticket doesn't exist!'
rollback tran
end
end
上邊我詳細介紹了樂觀鎖定和悲觀鎖定的使用方法,在實際生產環境里邊,如果並發量不大,我們完全可以使用悲觀鎖定的方法,因為這種方法使用起來非常方便和簡單.但是如果系統的並發非常大的話,悲觀鎖定會帶來非常大的性能問題,所以我們就要選擇樂觀鎖定的方法.
注意:
在使用其中的 SELECT 列表中具有 timestamp 列的 SELECT INTO 語句時,可能會生成重復的時間戳值。建議不要以這種方式使用 timestamp。
-----------------------------------
@@DBTS
備注
@@DBTS 返回當前數據庫最后使用的時間戳值。插入或更新包含 timestamp 列的行時,將產生一個新的時間戳值。
declare @timestamp timestamp
select @timestamp=timestamp字段 from 表 where 主鍵= 'xx '
update 表 set 字段= 'xx ' where 主鍵= 'xx ' and @timestamp=timestamp字段
if @@rowcount=0
print '已經有其他事務搶先提交了 '
set rowcount 2
select * from Course--相當於select top 2 * from Course
set rowcount 0//取消rowcount,不然每次只能查詢2行