SQL Server中流水號生成的注意事項
當(dāng)前位置:點晴教程→知識管理交流
→『 技術(shù)文檔交流 』
前幾天一個人問到了關(guān)于流水號重復(fù)的問題,我想了下,雖然說這個問題比較簡單,但是具有廣泛性,所以寫了這篇博客來介紹下,希望對大家有所幫助。 在進(jìn)行數(shù)據(jù)庫應(yīng)用開發(fā)時經(jīng)常會遇到生成流水號的情況,比如說做了一個訂單模塊,要求訂單號是唯一的,規(guī)則是:下訂單時的年月日+6位的流水號這樣的規(guī)則。 對于這種要生成流水號的系統(tǒng),我們一般是在數(shù)據(jù)庫中新建了一個種子表,每次生成新的訂單時: 1.讀取當(dāng)天種子最大值。 2.根據(jù)種子最大值和當(dāng)時的年月日生成唯一的訂單號。 3.更新種子最大值,使最大值+1。 4.根據(jù)生成的訂單號將訂單數(shù)據(jù)插入到訂單表中。 以上幾步操作是在一個事務(wù)中完成,保證了流水號的連續(xù)。這個思路是正確的,使用起來好像也沒有什么問題,但是在業(yè)務(wù)量比較大的情況下卻經(jīng)常報錯:“訂單號違反主鍵約束,不能將重復(fù)的訂單號插入到訂單表中?!边@是怎么回事?讓我們做一個簡單的Demo來重現(xiàn)一下: 1.創(chuàng)建種子表和訂單表,這里只是一個簡單的Demo,所以就省去了很多字段,而且訂單號假設(shè)就是一個流水號,不用再使用年月日+6位流水號了。
2.創(chuàng)建一個存儲過程,該存儲過程傳入Remark參數(shù),根據(jù)生成的流水號插入到訂單表中: CREATEPROCAddOrder--Author:深藍(lán) @remarkVARCHAR(5)--傳入的參數(shù) AS DECLARE@seekint BEGINTRAN --開啟一個事務(wù) SELECT@seek=SeekValue--讀取種子表中的最大值作為流水號 FROMSeek --生成訂單號這一步省略,因為這里假定的訂單的編號就是流水號 UPDATESeekSETSeekValue=@seek+1--更新種子表,使最大值+1 INSERTINTOt1VALUES(@seek,@remark)--插入一條訂單數(shù)據(jù) COMMIT--提交事務(wù) 3.新建一個查詢窗口,使用以下語句調(diào)用創(chuàng)建的存儲過程,不斷的插入新訂單: WHILE1=1 EXECAddOrder'test1'--不斷的插入訂單 4.再新建一個查詢窗口,使用通過的方式,不斷的插入新訂單,這樣用于模擬高并發(fā)時候的情況:
5.運行了一段時間后,我們停止這兩個死循環(huán),我們可以看到消息窗口中存在大量的異常: 消息 2627,級別 14,狀態(tài) 1,過程 AddOrder,第 11 行 違反了 PRIMARY KEY 約束 'PK__Orders__C3905BAF08EA5793'。不能在對象 'dbo.Orders' 中插入重復(fù)鍵。 語句已終止。 為什么會這樣呢?這得從事務(wù)隔離級別和鎖來解釋: 一般我們寫程序時都是使用的是默認(rèn)的事務(wù)隔離級別——已提交讀,在第一步查詢Seek表時,系統(tǒng)會為該表放置共享鎖,而鎖的兼容性中共享鎖和共享鎖是可以兼容的,所以一個事務(wù)在讀取Seek表最大值時,其他事務(wù)也可以讀取出相同的最大值,兩個事務(wù)中讀取到了相同的最大值,所以產(chǎn)生了相同的流水號,所以產(chǎn)生了相同的訂單號,所以才會出現(xiàn)違反主鍵約束的錯誤。 既然知道了這其中的原理了,那么解決辦法也就有了,只需要先對種子表中的數(shù)+1,然后再進(jìn)行讀取即可,修改存儲過程如下:
為什么這樣寫就可以呢?第一步執(zhí)行更新操作,系統(tǒng)會請求更新鎖然后再升級為排他鎖,因為更新鎖和更新鎖以及排他鎖都是不兼容的,所以一個事務(wù)對Seek表進(jìn)行了更新后,其他的事務(wù)就不能對表進(jìn)行更新操作,只有等到事務(wù)提交以后才能繼續(xù)。 這里附上鎖兼容性表:
該文章在 2011/3/3 17:38:45 編輯過 |
關(guān)鍵字查詢
相關(guān)文章
正在查詢... |