先引入一些概念,直接Copy其他Blogs中的,我就不單獨(dú)寫了。 一、為什么會有鎖 多個用戶同時對數(shù)據(jù)庫的并發(fā)操作時會帶來以下數(shù)據(jù)不一致的問題: 1.丟失更新 A,B兩個用戶讀同一數(shù)據(jù)并進(jìn)行修改,其中一個用戶的修改結(jié)果破壞了另一個修改的結(jié)果,比如訂票系統(tǒng) 2.臟讀 A用戶修改了數(shù)據(jù),隨后B用戶又讀出該數(shù)據(jù),但A用戶因為某些原因取消了對數(shù)據(jù)的修改,數(shù)據(jù)恢復(fù)原值,此時B得到的數(shù)據(jù)就與數(shù)據(jù)庫內(nèi)的數(shù)據(jù)產(chǎn)生了不一致 3.不可重復(fù)讀 A用戶讀取數(shù)據(jù),隨后B用戶讀出該數(shù)據(jù)并修改,此時A用戶再讀取數(shù)據(jù)時發(fā)現(xiàn)前后兩次的值4.不一致 并發(fā)控制的主要方法是封鎖,鎖就是在一段時間內(nèi)禁止用戶做某些操作以避免產(chǎn)生數(shù)據(jù)不一致
二、鎖的種類 共享 (S) 用于不更改或不更新數(shù)據(jù)的操作(只讀操作),如 SELECT 語句。 更新 (U) 用于可更新的資源中。防止當(dāng)多個會話在讀取、鎖定以及隨后可能進(jìn)行的資源更新時發(fā)生常見形式的死鎖。 排它 (X) 用于數(shù)據(jù)修改操作,例如 INSERT、UPDATE 或 DELETE。確保不會同時同一資源進(jìn)行多重更新。 意向鎖 用于建立鎖的層次結(jié)構(gòu)。意向鎖的類型為:意向共享 (IS)、意向排它 (IX) 以及與意向排它共享 (SIX)。 架構(gòu)鎖 在執(zhí)行依賴于表架構(gòu)的操作時使用。架構(gòu)鎖的類型為:架構(gòu)修改 (Sch-M) 和架構(gòu)穩(wěn)定性 (Sch-S)。 大容量更新 (BU) 向表中大容量復(fù)制數(shù)據(jù)并指定了 TABLOCK 提示時使用。 共享鎖 共享 (S) 鎖允許并發(fā)事務(wù)讀取 (SELECT) 一個資源。資源上存在共享 (S) 鎖時,任何其它事務(wù)都不能修改數(shù)據(jù)。一旦已經(jīng)讀取數(shù)據(jù),便立即釋放資源上的共享 (S) 鎖,除非將事務(wù)隔離級別設(shè)置為可重復(fù)讀或更高級別,或者在事務(wù)生存周期內(nèi)用鎖定提示保留共享 (S) 鎖。 更新鎖 更新 (U) 鎖可以防止通常形式的死鎖。一般更新模式由一個事務(wù)組成,此事務(wù)讀取記錄,獲取資源(頁或行)的共享 (S) 鎖,然后修改行,此操作要求鎖轉(zhuǎn)換為排它 (X) 鎖。如果兩個事務(wù)獲得了資源上的共享模式鎖,然后試圖同時更新數(shù)據(jù),則一個事務(wù)嘗試將鎖轉(zhuǎn)換為排它 (X) 鎖。共享模式到排它鎖的轉(zhuǎn)換必須等待一段時間,因為一個事務(wù)的排它鎖與其它事務(wù)的共享模式鎖不兼容;發(fā)生鎖等待。第二個事務(wù)試圖獲取排它 (X) 鎖以進(jìn)行更新。由于兩個事務(wù)都要轉(zhuǎn)換為排它 (X) 鎖,并且每個事務(wù)都等待另一個事務(wù)釋放共享模式鎖,因此發(fā)生死鎖。 若要避免這種潛在的死鎖問題,請使用更新 (U) 鎖。一次只有一個事務(wù)可以獲得資源的更新 (U) 鎖。如果事務(wù)修改資源,則更新 (U) 鎖轉(zhuǎn)換為排它 (X) 鎖。否則,鎖轉(zhuǎn)換為共享鎖。 排它鎖 排它 (X) 鎖可以防止并發(fā)事務(wù)對資源進(jìn)行訪問。其它事務(wù)不能讀取或修改排它 (X) 鎖鎖定的數(shù)據(jù)。 意向鎖 意向鎖表示 SQL Server 需要在層次結(jié)構(gòu)中的某些底層資源上獲取共享 (S) 鎖或排它 (X) 鎖。例如,放置在表級的共享意向鎖表示事務(wù)打算在表中的頁或行上放置共享 (S) 鎖。在表級設(shè)置意向鎖可防止另一個事務(wù)隨后在包含那一頁的表上獲取排它 (X) 鎖。意向鎖可以提高性能,因為 SQL Server 僅在表級檢查意向鎖來確定事務(wù)是否可以安全地獲取該表上的鎖。而無須檢查表中的每行或每頁上的鎖以確定事務(wù)是否可以鎖定整個表。 意向鎖包括意向共享 (IS)、意向排它 (IX) 以及與意向排它共享 (SIX)。
兩種由程序員定義的鎖 樂觀鎖:依靠表中數(shù)據(jù)行內(nèi)的版本戳或時間戳字段來人工管理鎖的工作。 悲觀鎖:使用數(shù)據(jù)庫或?qū)ο笊咸峁┑逆i機(jī)制來處理。 死鎖:死鎖的意思就是A用戶查找表1并獲得了S鎖,B用戶查找表1也獲得了S鎖,當(dāng)A用戶找到要更新的行申請X鎖時被告知B已經(jīng)有S鎖需要等待B解鎖,B用戶也找到要更新的行申請X鎖時被告知A已經(jīng)有了S鎖需要等待A解鎖,然后A與B就相互無休止的等待造成死鎖。
三、鎖的粒度也就是范圍 鎖粒度是被封鎖目標(biāo)的大小,封鎖粒度小則并發(fā)性高,但開銷大,封鎖粒度大則并發(fā)性低但開銷小 SQL Server支持的鎖粒度可以分為為行、頁、鍵、鍵范圍、索引、表或數(shù)據(jù)庫獲取鎖 RID 行標(biāo)識符。用于單獨(dú)鎖定表中的一行。 KEY 索引中的行鎖。用于保護(hù)可串行事務(wù)中的鍵范圍。 PAGE 8 千字節(jié) (KB) 的數(shù)據(jù)頁或索引頁。 EXTENT 相鄰的八個數(shù)據(jù)頁或索引頁構(gòu)成的一組。 TABLE 包括所有數(shù)據(jù)和索引在內(nèi)的整個表。 DATABASE 數(shù)據(jù)庫。
鎖的粒度和鎖的類型都是由SQL Server進(jìn)行控制的(當(dāng)然你也可以使用鎖提示,但不推薦)。鎖會給數(shù)據(jù)庫帶來阻塞,因此越大粒度的鎖造成更多的阻塞,但由于大粒度的鎖需要更少的鎖,因此會提升性能。而小粒度的鎖由于鎖定更少資源,會減少阻塞,因此提高了并發(fā),但同時大量的鎖也會造成性能的下降。
四、鎖的應(yīng)用 在使用SQL時,大都會遇到這樣的問題,你Update一條記錄時,需要通過Select來檢索出其值或條件,然后在通過這個值來執(zhí)行修改操作。 但當(dāng)以上操作放到多線程中并發(fā)處理時會出現(xiàn)問題:某線程select了一條記錄但還沒來得及update時,另一個線程仍然可能會進(jìn)來select到同一條記錄。 一般解決辦法就是使用鎖和事物的聯(lián)合機(jī)制: 1. 把select放在事務(wù)中, 否則select完成, 鎖就釋放了 BEGIN TRAN SELECT * FROM Table WITH(UPDLOCK) --或者 SELECT * FROM Table WITH(TABLOCKX, READPAST) 具體情況而定。 UPDATE .... COMMIT TRAN 所有Select加 With (NoLock)解決阻塞死鎖,在查詢語句中使用 NOLOCK 和 READPAST 1.NOLOCK 可能把沒有提交事務(wù)的數(shù)據(jù)也顯示出來 不使用 NOLOCK 和 READPAST ,在 Select 操作時候則有可能報錯誤:事務(wù)(進(jìn)程 ID **)與另一個進(jìn)程被死鎖在 鎖 資源上,并且已被選作死鎖犧牲品。 SELECT * FROM Table WITH(NOLOCK) SELECT * FROM Table WITH(READPAST) 鎖描述: HOLDLOCK:將共享鎖保留到事務(wù)完成,而不是在相應(yīng)的表、行或數(shù)據(jù)頁不再需要時就立即釋放鎖。HOLDLOCK等同于 SERIALIZABLE。
實(shí)際開始動手用代碼說話吧! SQLServer2012在查詢分析器里面開兩個連接 插入鎖: 結(jié)論:“表鎖”鎖定對該表的Select、Update、Delete操作,但不影響對該表的Insert操作也不影響以主鍵Id為條件的Select,所以Select如果不想等待就要在Select后加With(Nolock),但這樣會產(chǎn)生臟數(shù)據(jù)就是其他事務(wù)已更新但并沒有提交的數(shù)據(jù),如果該事務(wù)進(jìn)行了RollBack則取出的數(shù)據(jù)就是錯誤的,所以好自己權(quán)衡利弊,一般情況下90%以上的Select都允許臟讀,只有賬戶金額相關(guān)的不允許。 ------------------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 ) --可以執(zhí)行插入 SELECT * FROM dbo.UserInfo --需要等待解鎖 SELECT * FROM dbo.UserInfo WHERE Age=1 --需要等待解鎖 SELECT * FROM dbo.UserInfo WHERE Id=3 --可以執(zhí)行查詢(根據(jù)主鍵可以) SELECT * FROM dbo.UserInfo WITH(NOLOCK) --可以執(zhí)行查詢(在一個事務(wù)中,有更新字段但還沒有提交,此時就會查處臟數(shù)據(jù)) SELECT * FROM dbo.UserInfo WITH(NOLOCK) WHERE Age=1 --可以執(zhí)行查詢 UPDATE dbo.UserInfo SET Type=5 WHERE Name='fff' --需要等待解鎖 DELETE FROM dbo.UserInfo WHERE Name='fff' --需要等待解鎖 更新鎖: 結(jié)論:“表鎖”鎖定對該表的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 ) --可以執(zhí)行插入 SELECT * FROM dbo.UserInfo --需要等待解鎖 SELECT * FROM dbo.UserInfo WHERE Name='ppp' --需要等待解鎖 SELECT * FROM dbo.UserInfo WHERE Id=3 --可以執(zhí)行查詢(根據(jù)主鍵可以) SELECT * FROM dbo.UserInfo WITH(NOLOCK) --可以執(zhí)行查詢(在一個事務(wù)中,有更新字段但還沒有提交,此時就會查處臟數(shù)據(jù)) SELECT * FROM dbo.UserInfo WITH(NOLOCK) WHERE Name = 'ppp' --可以執(zhí)行查詢 UPDATE dbo.UserInfo SET Age=8 WHERE Name='ccc' --需要等待解鎖 DELETE dbo.UserInfo WHERE Age = 5 --需要等待解鎖 主鍵鎖: 結(jié)論:“行鎖+表鎖” 鎖定對該表的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 ) --可以執(zhí)行插入 SELECT * FROM dbo.UserInfo WITH(NOLOCK) --可以執(zhí)行查詢(在一個事務(wù)中,有更新字段但還沒有提交,此時就會查處臟數(shù)據(jù)) SELECT * FROM dbo.UserInfo WITH(NOLOCK) WHERE Name = 'kkk' --可以執(zhí)行查詢 -----//全表查詢及操作正在處理的行 SELECT * FROM dbo.UserInfo --需要等待解鎖 SELECT * FROM dbo.UserInfo WHERE Id=3 --需要等待解鎖(根據(jù)主鍵,但與A連接操作相同行不可) UPDATE dbo.UserInfo SET Name='mmm' WHERE Id=3 --需要等待解鎖(根據(jù)主鍵,但與A連接操作相同行不可) DELETE dbo.UserInfo WHERE Id=3 --需要等待解鎖(根據(jù)主鍵,但與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 --可以執(zhí)行查詢(根據(jù)主鍵可以) UPDATE dbo.UserInfo SET Name='yyy' WHERE Id=1 --可以執(zhí)行更新(根據(jù)主鍵可以) DELETE dbo.UserInfo WHERE Id=1 --可以執(zhí)行刪除(根據(jù)主鍵可以) 索引鎖: 結(jié)論:“行鎖+表鎖” 鎖定對該表的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 ) --可以執(zhí)行插入 SELECT * FROM dbo.UserInfo WITH(NOLOCK) --可以執(zhí)行查詢(在一個事物中,有更新字段但還沒有提交,此時就會查處臟數(shù)據(jù)) SELECT * FROM dbo.UserInfo WITH(NOLOCK) WHERE Name = 'kkk' --可以執(zhí)行查詢 -----//全表查詢及操作正在處理的行 SELECT * FROM dbo.UserInfo --需要等待解鎖 SELECT * FROM dbo.UserInfo WHERE Id=4 --需要等待解鎖(根據(jù)主鍵,但與A連接操作相同行不可) UPDATE dbo.UserInfo SET Name='mmm' WHERE Id=4 --需要等待解鎖(根據(jù)主鍵,但與A連接操作相同行不可) DELETE dbo.UserInfo WHERE Id=4 --需要等待解鎖(根據(jù)主鍵,但與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 --可以執(zhí)行更新(根據(jù)主鍵可以) UPDATE dbo.UserInfo SET Name='yyy' WHERE Id=1 --可以執(zhí)行更新(根據(jù)主鍵可以) DELETE dbo.UserInfo WHERE Id=1 --可以執(zhí)行刪除(根據(jù)主鍵可以) -----//使用索引為條件的操作 SELECT * FROM dbo.UserInfo WHERE Name='aaa' --需要等待解鎖(非主鍵不可) UPDATE dbo.UserInfo SET Name='ooo' WHERE Name='aaa' --可以執(zhí)行更新(根據(jù)索引可以) DELETE dbo.UserInfo WHERE Name='aaa' --可以執(zhí)行刪除(根據(jù)索引可以) 悲觀鎖(更新鎖-人工手動設(shè)置上鎖): 結(jié)論:可以理解為在使用版本控制軟件的時候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 --可以執(zhí)行查詢 SELECT * FROM dbo.UserInfo WHERE id=2 --可以執(zhí)行查詢 SELECT * FROM dbo.UserInfo WHERE Name='ooo' --可以執(zhí)行查詢 UPDATE dbo.UserInfo SET Age=3 WHERE id=1 --可以執(zhí)行更新(根據(jù)主鍵可以) UPDATE dbo.UserInfo SET Age=3 WHERE Name='ccc' --需要等待解鎖(非主鍵不可) DELETE dbo.UserInfo WHERE id=1 --可以執(zhí)行更新(根據(jù)主鍵可以) DELETE dbo.UserInfo WHERE name='ccc' --需要等待解鎖(非主鍵不可) 樂觀鎖(人工通過邏輯在數(shù)據(jù)庫中模擬鎖) 結(jié)論:可以理解為同樣在使用版本控制軟件的時候A遷出了一個文件,B也可以遷出該文件,兩個人都可以對此文件進(jìn)行修改,其中一個人先進(jìn)行提交的時候,版本并沒有變化所以可以正常提交,另一個后提交的時候,發(fā)現(xiàn)版本增加不對稱了,就提示沖突由用戶來選擇如何進(jìn)行合并再重新進(jìn)行提交。 --------------------------A客戶端連接 Lock(樂觀鎖)------------------------ --DROP TABLE Coupon -----------------創(chuàng)建優(yōu)惠券表----------------- 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) --查詢數(shù)據(jù) --UPDATE Coupon SET [User]=NULL, UseTime=NULL, IsFlag=0 --還原數(shù)據(jù) -----------------1、模擬高并發(fā)普通更新----------------- DECLARE @User VARCHAR(50) --模擬要使用優(yōu)惠券的用戶 DECLARE @TempId INT --模擬抽選出來的要使用的優(yōu)惠券 SET @User='a' BEGIN TRAN SELECT @TempId=Id FROM dbo.Coupon WHERE IsFlag=0 --高并發(fā)時此語句有可能另外一個該事務(wù)已取出的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) --模擬要使用優(yōu)惠券的用戶 DECLARE @TempId INT --模擬抽選出來的要使用的優(yōu)惠券 SET @User='a' BEGIN TRAN SELECT @TempId=Id FROM dbo.Coupon WITH(UPDLOCK) WHERE IsFlag=0 --高并發(fā)時此語句會鎖定取出的Id數(shù)據(jù)行 --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 --增加數(shù)據(jù)行版本戳類型字段(微軟新推薦數(shù)據(jù)字段,該字段每張表只能有一個,會在創(chuàng)建行或更新行時自動進(jìn)行修改無需人為干涉,該字段不能建立索引及主鍵因為會頻繁修改) DECLARE @User VARCHAR(50) --模擬要使用優(yōu)惠券的用戶 DECLARE @TempId INT --模擬抽選出來的要使用的優(yōu)惠券 DECLARE @RowVer BINARY(8) --抽選出來的優(yōu)惠券的版本(ROWVERSION數(shù)據(jù)類型存儲大小為8字節(jié)) SET @User='a' BEGIN TRY BEGIN TRAN SELECT @TempId=Id, @RowVer=RowVer FROM dbo.Coupon WHERE IsFlag=0 --取出可用的Id及對應(yīng)的版本戳 --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('該數(shù)據(jù)已被其他用戶修改') ROLLBACK TRAN END END TRY BEGIN CATCH ROLLBACK TRAN END CATCH --------------------------B客戶端連接 Lock(樂觀鎖)------------------------ --此測試需要開兩個SQL Management Studio客戶端,在A客戶端使用WAITFOR DELAY來模擬并發(fā)占用,在B客戶端執(zhí)行與A客戶端相同的SQL腳本即可(注釋掉WAITFOR),所以在此不放相同代碼了。 在樂觀鎖和悲觀鎖之間進(jìn)行選擇的標(biāo)準(zhǔn)是:沖突的頻率與嚴(yán)重性。如果沖突很少,或者沖突的后果不會很嚴(yán)重,那么通常情況下應(yīng)該選擇樂觀鎖,因為它能得到更好的并發(fā)性,而且更容易實(shí)現(xiàn)。但是,如果沖突的結(jié)果對于用戶來說痛苦的,那么就需要使用悲觀策略。 我認(rèn)為如果同一張表的并發(fā)很高,但并發(fā)處理同一條數(shù)據(jù)的沖突幾率很低,那就應(yīng)該使用樂觀鎖,反之,如果同一張表的并發(fā)不高,但同時處理同一條數(shù)據(jù)的幾率很高,就應(yīng)該使用悲觀鎖。
|
|