https://www.cnblogs.com/taiyonghai/p/5674462.html
先引入一些概念,直接Copy其他Blogs中的,我就不單獨寫了。
一、為什麼會有鎖
多個使用者同時對資料庫的併發操作時會帶來以下資料不一致的問題:
1.丟失更新
A,B兩個使用者讀同一資料並進行修改,其中一個使用者的修改結果破壞了另一個修改的結果,比如訂票系統
2.髒讀
A使用者修改了資料,隨後B使用者又讀出該資料,但A使用者因為某些原因取消了對資料的修改,資料恢復原值,此時B得到的資料就與資料庫內的資料產生了不一致
3.不可重複讀
A使用者讀取資料,隨後B使用者讀出該資料並修改,此時A使用者再讀取資料時發現前後兩次的值4.不一致
併發控制的主要方法是封鎖,鎖就是在一段時間內禁止使用者做某些操作以避免產生資料不一致
二、鎖的種類
共享 (S) 用於不更改或不更新資料的操作(只讀操作),如 SELECT 語句。
更新 (U) 用於可更新的資源中。防止當多個會話在讀取、鎖定以及隨後可能進行的資源更新時發生常見形式的死鎖。
排它 (X) 用於資料修改操作,例如 INSERT、UPDATE 或 DELETE。確保不會同時同一資源進行多重更新。
意向鎖 用於建立鎖的層次結構。意向鎖的型別為:意向共享 (IS)、意向排它 (IX) 以及與意向排它共享 (SIX)。
架構鎖 在執行依賴於表架構的操作時使用。架構鎖的型別為:架構修改 (Sch-M) 和架構穩定性 (Sch-S)。
大容量更新 (BU) 向表中大容量複製資料並指定了 TABLOCK 提示時使用。
共享鎖
共享 (S) 鎖允許併發事務讀取 (SELECT) 一個資源。資源上存在共享 (S) 鎖時,任何其它事務都不能修改資料。一旦已經讀取資料,便立即釋放資源上的共享 (S) 鎖,除非將事務隔離級別設定為可重複讀或更高階別,或者在事務生存週期內用鎖定提示保留共享 (S) 鎖。
更新鎖
更新 (U) 鎖可以防止通常形式的死鎖。一般更新模式由一個事務組成,此事務讀取記錄,獲取資源(頁或行)的共享 (S) 鎖,然後修改行,此操作要求鎖轉換為排它 (X) 鎖。如果兩個事務獲得了資源上的共享模式鎖,然後試圖同時更新資料,則一個事務嘗試將鎖轉換為排它 (X) 鎖。共享模式到排它鎖的轉換必須等待一段時間,因為一個事務的排它鎖與其它事務的共享模式鎖不相容;發生鎖等待。第二個事務試圖獲取排它 (X) 鎖以進行更新。由於兩個事務都要轉換為排它 (X) 鎖,並且每個事務都等待另一個事務釋放共享模式鎖,因此發生死鎖。
若要避免這種潛在的死鎖問題,請使用更新 (U) 鎖。一次只有一個事務可以獲得資源的更新 (U) 鎖。如果事務修改資源,則更新 (U) 鎖轉換為排它 (X) 鎖。否則,鎖轉換為共享鎖。
排它鎖
排它 (X) 鎖可以防止併發事務對資源進行訪問。其它事務不能讀取或修改排它 (X) 鎖鎖定的資料。
意向鎖
意向鎖表示 SQL Server 需要在層次結構中的某些底層資源上獲取共享 (S) 鎖或排它 (X) 鎖。例如,放置在表級的共享意向鎖表示事務打算在表中的頁或行上放置共享 (S) 鎖。在表級設定意向鎖可防止另一個事務隨後在包含那一頁的表上獲取排它 (X) 鎖。意向鎖可以提高效能,因為 SQL Server 僅在表級檢查意向鎖來確定事務是否可以安全地獲取該表上的鎖。而無須檢查表中的每行或每頁上的鎖以確定事務是否可以鎖定整個表。
意向鎖包括意向共享 (IS)、意向排它 (IX) 以及與意向排它共享 (SIX)。
兩種由程式設計師定義的鎖
樂觀鎖:依靠表中資料行內的版本戳或時間戳欄位來人工管理鎖的工作。
悲觀鎖:使用資料庫或物件上提供的鎖機制來處理。
死鎖:死鎖的意思就是A使用者查詢表1並獲得了S鎖,B使用者查詢表1也獲得了S鎖,當A使用者找到要更新的行申請X鎖時被告知B已經有S鎖需要等待B解鎖,B使用者也找到要更新的行申請X鎖時被告知A已經有了S鎖需要等待A解鎖,然後A與B就相互無休止的等待造成死鎖。
三、鎖的粒度也就是範圍
鎖粒度是被封鎖目標的大小,封鎖粒度小則併發性高,但開銷大,封鎖粒度大則併發性低但開銷小
SQL Server支援的鎖粒度可以分為為行、頁、鍵、鍵範圍、索引、表或資料庫獲取鎖
RID 行識別符號。用於單獨鎖定表中的一行。
KEY 索引中的行鎖。用於保護可序列事務中的鍵範圍。
PAGE 8 千位元組 (KB) 的資料頁或索引頁。
EXTENT 相鄰的八個資料頁或索引頁構成的一組。
TABLE 包括所有資料和索引在內的整個表。
DATABASE 資料庫。
鎖的粒度和鎖的型別都是由SQL Server進行控制的(當然你也可以使用鎖提示,但不推薦)。鎖會給資料庫帶來阻塞,因此越大粒度的鎖造成更多的阻塞,但由於大粒度的鎖需要更少的鎖,因此會提升效能。而小粒度的鎖由於鎖定更少資源,會減少阻塞,因此提高了併發,但同時大量的鎖也會造成效能的下降。
四、鎖的應用
在使用SQL時,大都會遇到這樣的問題,你Update一條記錄時,需要透過Select來檢索出其值或條件,然後在透過這個值來執行修改操作。
但當以上操作放到多執行緒中併發處理時會出現問題:某執行緒select了一條記錄但還沒來得及update時,另一個執行緒仍然可能會進來select到同一條記錄。
一般解決辦法就是使用鎖和事物的聯合機制:
1. 把select放在事務中, 否則select完成, 鎖就釋放了
2. 要阻止另一個select , 則要手工加鎖, select 預設是共享鎖, select之間的共享鎖是不衝突的, 所以, 如果只是共享鎖, 即使鎖沒有釋放, 另一個select一樣可以下共享鎖, 從而select出資料
BEGIN TRAN SELECT * FROM Table WITH(UPDLOCK) --或者 SELECT * FROM Table WITH(TABLOCKX, READPAST) 具體情況而定。 UPDATE .... COMMIT TRAN
所有Select加 With (NoLock)解決阻塞死鎖,在查詢語句中使用 NOLOCK 和 READPAST
處理一個資料庫死鎖的異常時候,其中一個建議就是使用 NOLOCK 或者 READPAST 。有關 NOLOCK 和 READPAST的一些技術知識點:
對於非銀行等嚴格要求事務的行業,搜尋記錄中出現或者不出現某條記錄,都是在可容忍範圍內,所以碰到死鎖,應該首先考慮,我們業務邏輯是否能容忍出現或者不出現某些記錄,而不是尋求對雙方都加鎖條件下如何解鎖的問題。
NOLOCK 和 READPAST 都是處理查詢、插入、刪除等操作時候,如何應對鎖住的資料記錄。但是這時候一定要注意NOLOCK 和 READPAST的侷限性,確認你的業務邏輯可以容忍這些記錄的出現或者不出現:
簡單來說:
1.NOLOCK 可能把沒有提交事務的資料也顯示出來
2.READPAST 會把被鎖住的行不顯示出來
不使用 NOLOCK 和 READPAST ,在 Select 操作時候則有可能報錯誤:事務(程序 ID **)與另一個程序被死鎖在 鎖 資源上,並且已被選作死鎖犧牲品。
SELECT * FROM Table WITH(NOLOCK) SELECT * FROM Table WITH(READPAST)
鎖描述:
HOLDLOCK:將共享鎖保留到事務完成,而不是在相應的表、行或資料頁不再需要時就立即釋放鎖。HOLDLOCK等同於 SERIALIZABLE。
NOLOCK 不要發出共享鎖,並且不要提供排它鎖。當此選項生效時,可能會讀取未提交的事務或一組在讀取中間回滾的頁面。有可能發生髒讀。僅應用於 SELECT 語句。
PAGLOCK:在通常使用單個表鎖的地方採用頁鎖。
READCOMMITTED:用與執行在提交讀隔離級別的事務相同的鎖語義執行掃描。預設情況下,SQL Server 2000在此隔離級別上操作。
READPAST:跳過鎖定行。此選項導致事務跳過由其它事務鎖定的行(這些行平常會顯示在結果集內),而不是阻塞該事務,使其等待其它事務釋放在這些行上的鎖。 READPAST 鎖提示僅適用於執行在提交讀隔離級別的事務,並且只在行級鎖之後讀取。僅適用於 SELECT 語句。
READUNCOMMITTED:等同於 NOLOCK。
REPEATABLEREAD:用與執行在可重複讀隔離級別的事務相同的鎖語義執行掃描。
ROWLOCK:使用行級鎖,而不使用粒度更粗的頁級鎖和表級鎖。
SERIALIZABLE:用與執行在可序列讀隔離級別的事務相同的鎖語義執行掃描。等同於 HOLDLOCK。
TABLOCK:使用表鎖代替粒度更細的行級鎖或頁級鎖。在語句結束前,SQL Server 一直持有該鎖。但是,如果同時指定 HOLDLOCK,那麼在事務結束之前,鎖將被一直持有。
TABLOCKX 使用表的排它鎖。該鎖可以防止其它事務讀取或更新表,並在語句或事務結束前一直持有。
UPDLOCK:讀取表時使用更新鎖,而不使用共享鎖,並將鎖一直保留到語句或事務的結束。UPDLOCK:的優點是允許您讀取資料(不阻塞其它事務)並在以後更新資料,同時確保自從上次讀取資料後資料沒有被更改。
XLOCK:使用排它鎖並一直保持到由語句處理的所有資料上的事務結束時。可以使用 PAGLOCK 或 TABLOCK 指定該鎖,這種情況下排它鎖適用於適當級別的粒度。
實際開始動手用程式碼說話吧!
SQLServer2012在查詢分析器裡面開兩個連線
插入鎖:
結論:“表鎖”鎖定對該表的Select、Update、Delete操作,但不影響對該表的Insert操作也不影響以主鍵Id為條件的Select,所以Select如果不想等待就要在Select後加With(Nolock),但這樣會產生髒資料就是其他事務已更新但並沒有提交的資料,如果該事務進行了RollBack則取出的資料就是錯誤的,所以好自己權衡利弊,一般情況下90%以上的Select都允許髒讀,只有賬戶金額相關的不允許。
------------------A連線 Insert Lock------------------- BEGIN TRAN INSERT INTO dbo.UserInfo ( Name, Age, Mobile, AddTime, Type ) VALUES ( 'eee', -- Name - varchar(50) 2, -- Age - int '555', -- Mobile - char(11) GETDATE(), -- AddTime - datetime 0 -- Type - int ) SELECT resource_type, request_mode,COUNT(*) FROM sys.dm_tran_locks WHERE request_session_id=@@SPID GROUP BY resource_type,request_mode --ROLLBACK TRAN ------------------------B連線 Insert Lock------------------------ INSERT INTO dbo.UserInfo ( Name, Age, Mobile, AddTime, Type ) VALUES ( 'fff', -- Name - varchar(50) 2, -- Age - int '123', -- Mobile - char(11) GETDATE(), -- AddTime - datetime 1 -- Type - int ) --可以執行插入 SELECT * FROM dbo.UserInfo --需要等待解鎖 SELECT * FROM dbo.UserInfo WHERE Age=1 --需要等待解鎖 SELECT * FROM dbo.UserInfo WHERE Id=3 --可以執行查詢(根據主鍵可以) SELECT * FROM dbo.UserInfo WITH(NOLOCK) --可以執行查詢(在一個事務中,有更新欄位但還沒有提交,此時就會查處髒資料) SELECT * FROM dbo.UserInfo WITH(NOLOCK) WHERE Age=1 --可以執行查詢 UPDATE dbo.UserInfo SET Type=5 WHERE Name='fff' --需要等待解鎖 DELETE FROM dbo.UserInfo WHERE Name='fff' --需要等待解鎖
更新鎖:
結論:“表鎖”鎖定對該表的Select、Update、Delete操作,但不影響對該表的Insert操作也不影響以主鍵Id為條件的Select
-----------------------A連線 Update Lock----------------------- BEGIN TRAN UPDATE dbo.UserInfo SET Name = 'eee' WHERE Age = 2 SELECT resource_type, request_mode,COUNT(*) FROM sys.dm_tran_locks WHERE request_session_id=@@SPID GROUP BY resource_type,request_mode --ROLLBACK TRAN ------------------------B連線 Update Lock------------------------ INSERT INTO dbo.UserInfo ( Name, Age, Mobile, AddTime, Type ) VALUES ( 'ppp', -- Name - varchar(50) 15, -- Age - int '666', -- Mobile - char(11) GETDATE(), -- AddTime - datetime 9 -- Type - int ) --可以執行插入 SELECT * FROM dbo.UserInfo --需要等待解鎖 SELECT * FROM dbo.UserInfo WHERE Name='ppp' --需要等待解鎖 SELECT * FROM dbo.UserInfo WHERE Id=3 --可以執行查詢(根據主鍵可以) SELECT * FROM dbo.UserInfo WITH(NOLOCK) --可以執行查詢(在一個事務中,有更新欄位但還沒有提交,此時就會查處髒資料) SELECT * FROM dbo.UserInfo WITH(NOLOCK) WHERE Name = 'ppp' --可以執行查詢 UPDATE dbo.UserInfo SET Age=8 WHERE Name='ccc' --需要等待解鎖 DELETE dbo.UserInfo WHERE Age = 5 --需要等待解鎖
主鍵鎖:
結論:“行鎖+表鎖” 鎖定對該表的Select、Update、Delete操作,但不影響對該表的Insert操作也不影響以主鍵Id為條件的Select、Update、Delete
------------------------A連線 Key Lock-------------------- BEGIN TRAN UPDATE dbo.UserInfo SET Name='hhh' WHERE Id=3 --以主鍵為條件 SELECT resource_type, request_mode,COUNT(*) FROM sys.dm_tran_locks WHERE request_session_id=@@SPID GROUP BY resource_type,request_mode --ROLLBACK TRAN ------------------------B連線 Key Lock---------------------- INSERT INTO dbo.UserInfo ( Name, Age, Mobile, AddTime, Type ) VALUES ( 'kkk', -- Name - varchar(50) 18, -- Age - int '234', -- Mobile - char(11) GETDATE(), -- AddTime - datetime 7 -- Type - int ) --可以執行插入 SELECT * FROM dbo.UserInfo WITH(NOLOCK) --可以執行查詢(在一個事務中,有更新欄位但還沒有提交,此時就會查處髒資料) SELECT * FROM dbo.UserInfo WITH(NOLOCK) WHERE Name = 'kkk' --可以執行查詢 -----//全表查詢及操作正在處理的行 SELECT * FROM dbo.UserInfo --需要等待解鎖 SELECT * FROM dbo.UserInfo WHERE Id=3 --需要等待解鎖(根據主鍵,但與A連線操作相同行不可) UPDATE dbo.UserInfo SET Name='mmm' WHERE Id=3 --需要等待解鎖(根據主鍵,但與A連線操作相同行不可) DELETE dbo.UserInfo WHERE Id=3 --需要等待解鎖(根據主鍵,但與A連線操作相同行不可) -----//使用非主鍵為條件的操作 SELECT * FROM dbo.UserInfo WHERE Name='aaa' --需要等待解鎖(非主鍵不可) UPDATE dbo.UserInfo SET Name='ooo' WHERE Name='aaa' --需要等待解鎖(非主鍵不可) DELETE dbo.UserInfo WHERE Name='aaa' --需要等待解鎖(非主鍵不可) -----//使用主鍵為條件的操作 SELECT * FROM dbo.UserInfo WHERE id=1 --可以執行查詢(根據主鍵可以) UPDATE dbo.UserInfo SET Name='yyy' WHERE Id=1 --可以執行更新(根據主鍵可以) DELETE dbo.UserInfo WHERE Id=1 --可以執行刪除(根據主鍵可以)
索引鎖:
結論:“行鎖+表鎖” 鎖定對該表的Select、Update、Delete操作,但不影響對該表的Insert操作也不影響以主鍵Id為條件的Select、Update、Delete,也不影響以索引列Name為條件的Update、Delete但不可以Select
------------------------A連線 Index Lock-------------------- DROP INDEX dbo.UserInfo.Index_UserInfo_Name CREATE INDEX Index_UserInfo_Name ON dbo.UserInfo(Name) BEGIN TRAN UPDATE dbo.UserInfo SET age=66 WHERE Name='ddd' --使用name索引列為條件 SELECT resource_type, request_mode,COUNT(*) FROM sys.dm_tran_locks WHERE request_session_id=@@SPID GROUP BY resource_type,request_mode --ROLLBACK TRAN ----------------------B連線 Index Lock------------------- INSERT INTO dbo.UserInfo ( Name, Age, Mobile, AddTime, Type ) VALUES ( 'iii', -- Name - varchar(50) 20, -- Age - int '235235235', -- Mobile - char(11) GETDATE(), -- AddTime - datetime 12 -- Type - int ) --可以執行插入 SELECT * FROM dbo.UserInfo WITH(NOLOCK) --可以執行查詢(在一個事物中,有更新欄位但還沒有提交,此時就會查處髒資料) SELECT * FROM dbo.UserInfo WITH(NOLOCK) WHERE Name = 'kkk' --可以執行查詢 -----//全表查詢及操作正在處理的行 SELECT * FROM dbo.UserInfo --需要等待解鎖 SELECT * FROM dbo.UserInfo WHERE Id=4 --需要等待解鎖(根據主鍵,但與A連線操作相同行不可) UPDATE dbo.UserInfo SET Name='mmm' WHERE Id=4 --需要等待解鎖(根據主鍵,但與A連線操作相同行不可) DELETE dbo.UserInfo WHERE Id=4 --需要等待解鎖(根據主鍵,但與A連線操作相同行不可) -----//使用非主鍵非索引為條件的操作 SELECT * FROM dbo.UserInfo WHERE Age=5 --需要等待解鎖(非主鍵不可) UPDATE dbo.UserInfo SET Name='ooo' WHERE Age=5 --需要等待解鎖(非主鍵不可) DELETE dbo.UserInfo WHERE Age=5 --需要等待解鎖(非主鍵不可) -----//使用主鍵為條件的操作 SELECT * FROM dbo.UserInfo WHERE Id=1 --可以執行更新(根據主鍵可以) UPDATE dbo.UserInfo SET Name='yyy' WHERE Id=1 --可以執行更新(根據主鍵可以) DELETE dbo.UserInfo WHERE Id=1 --可以執行刪除(根據主鍵可以) -----//使用索引為條件的操作 SELECT * FROM dbo.UserInfo WHERE Name='aaa' --需要等待解鎖(非主鍵不可) UPDATE dbo.UserInfo SET Name='ooo' WHERE Name='aaa' --可以執行更新(根據索引可以) DELETE dbo.UserInfo WHERE Name='aaa' --可以執行刪除(根據索引可以)
悲觀鎖(更新鎖-人工手動設定上鎖):
結論:可以理解為在使用版本控制軟體的時候A遷出了一個檔案,並且將這個檔案鎖定,B就無法再遷出該檔案了,直到A遷入解鎖後才能被其他人遷出。
------------------------A連線 Update Lock(悲觀鎖)--------------------- BEGIN TRAN SELECT * FROM dbo.UserInfo WITH(UPDLOCK) WHERE Id=2 SELECT resource_type, request_mode,COUNT(*) FROM sys.dm_tran_locks WHERE request_session_id=@@SPID GROUP BY resource_type,request_mode --COMMIT TRAN --ROLLBACK TRAN ---------------------------B連線 Update Lock(悲觀鎖)------------------------- SELECT * FROM dbo.UserInfo --可以執行查詢 SELECT * FROM dbo.UserInfo WHERE id=2 --可以執行查詢 SELECT * FROM dbo.UserInfo WHERE Name='ooo' --可以執行查詢 UPDATE dbo.UserInfo SET Age=3 WHERE id=1 --可以執行更新(根據主鍵可以) UPDATE dbo.UserInfo SET Age=3 WHERE Name='ccc' --需要等待解鎖(非主鍵不可) DELETE dbo.UserInfo WHERE id=1 --可以執行更新(根據主鍵可以) DELETE dbo.UserInfo WHERE name='ccc' --需要等待解鎖(非主鍵不可)
樂觀鎖(人工透過邏輯在資料庫中模擬鎖)
結論:可以理解為同樣在使用版本控制軟體的時候A遷出了一個檔案,B也可以遷出該檔案,兩個人都可以對此檔案進行修改,其中一個人先進行提交的時候,版本並沒有變化所以可以正常提交,另一個後提交的時候,發現版本增加不對稱了,就提示衝突由使用者來選擇如何進行合併再重新進行提交。
--------------------------A客戶端連線 Lock(樂觀鎖)------------------------ --DROP TABLE Coupon -----------------建立優惠券表----------------- CREATE TABLE Coupon ( Id INT PRIMARY KEY IDENTITY(1,1), Number VARCHAR(50) NOT NULL, [User] VARCHAR(50), UseTime DATETIME, IsFlag BIT DEFAULT(0) NOT NULL, CreateTime DATETIME DEFAULT(GETDATE()) NOT NULL ) INSERT INTO dbo.Coupon(Number) VALUES ( '10000001') INSERT INTO dbo.Coupon(Number) VALUES ( '10000002') INSERT INTO dbo.Coupon(Number) VALUES ( '10000003') INSERT INTO dbo.Coupon(Number) VALUES ( '10000004') INSERT INTO dbo.Coupon(Number) VALUES ( '10000005') INSERT INTO dbo.Coupon(Number) VALUES ( '10000006') --SELECT * FROM dbo.Coupon WITH(NOLOCK) --查詢資料 --UPDATE Coupon SET [User]=NULL, UseTime=NULL, IsFlag=0 --還原資料 -----------------1、模擬高併發普通更新----------------- DECLARE @User VARCHAR(50) --模擬要使用優惠券的使用者 DECLARE @TempId INT --模擬抽選出來的要使用的優惠券 SET @User='a' BEGIN TRAN SELECT @TempId=Id FROM dbo.Coupon WHERE IsFlag=0 --高併發時此語句有可能另外一個該事務已取出的Id --WAITFOR DELAY '00:00:05' --改用此方式要開兩個SQL Management客戶端 UPDATE dbo.Coupon SET IsFlag=1, [User]=@User, UseTime=GETDATE() WHERE Id=@TempId COMMIT TRAN --ROLLBACK TRAN -----------------2、悲觀鎖解決方案----------------- DECLARE @User VARCHAR(50) --模擬要使用優惠券的使用者 DECLARE @TempId INT --模擬抽選出來的要使用的優惠券 SET @User='a' BEGIN TRAN SELECT @TempId=Id FROM dbo.Coupon WITH(UPDLOCK) WHERE IsFlag=0 --高併發時此語句會鎖定取出的Id資料行 --WAITFOR DELAY '00:00:05' --改用此方式要開兩個SQL Management客戶端 UPDATE dbo.Coupon SET IsFlag=1, [User]=@User, UseTime=GETDATE() WHERE Id=@TempId COMMIT TRAN --ROLLBACK TRAN -----------------3、樂觀鎖解決方案----------------- ALTER TABLE dbo.Coupon ADD RowVer ROWVERSION NOT NULL --增加資料行版本戳型別欄位(微軟新推薦資料欄位,該欄位每張表只能有一個,會在建立行或更新行時自動進行修改無需人為干涉,該欄位不能建立索引及主鍵因為會頻繁修改) DECLARE @User VARCHAR(50) --模擬要使用優惠券的使用者 DECLARE @TempId INT --模擬抽選出來的要使用的優惠券 DECLARE @RowVer BINARY(8) --抽選出來的優惠券的版本(ROWVERSION資料型別儲存大小為8位元組) SET @User='a' BEGIN TRY BEGIN TRAN SELECT @TempId=Id, @RowVer=RowVer FROM dbo.Coupon WHERE IsFlag=0 --取出可用的Id及對應的版本戳 --WAITFOR DELAY '00:00:05' --改用此方式要開兩個SQL Management客戶端 UPDATE dbo.Coupon SET IsFlag=1, [User]=@User, UseTime=GETDATE() WHERE Id=@TempId AND RowVer=@RowVer IF(@@ROWCOUNT > 0) BEGIN PRINT('修改成功') COMMIT TRAN END ELSE BEGIN PRINT('該資料已被其他使用者修改') ROLLBACK TRAN END END TRY BEGIN CATCH ROLLBACK TRAN END CATCH --------------------------B客戶端連線 Lock(樂觀鎖)------------------------ --此測試需要開兩個SQL Management Studio客戶端,在A客戶端使用WAITFOR DELAY來模擬併發佔用,在B客戶端執行與A客戶端相同的SQL指令碼即可(註釋掉WAITFOR),所以在此不放相同程式碼了。
在樂觀鎖和悲觀鎖之間進行選擇的標準是:衝突的頻率與嚴重性。如果衝突很少,或者衝突的後果不會很嚴重,那麼通常情況下應該選擇樂觀鎖,因為它能得到更好的併發性,而且更容易實現。但是,如果衝突的結果對於使用者來說痛苦的,那麼就需要使用悲觀策略。
我認為如果同一張表的併發很高,但併發處理同一條資料的衝突機率很低,那就應該使用樂觀鎖,反之,如果同一張表的併發不高,但同時處理同一條資料的機率很高,就應該使用悲觀鎖。