乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      SQL Server 鎖機(jī)制 悲觀鎖 樂觀鎖 實(shí)測解析

       咸咸咸咸魚干 2019-05-15

      先引入一些概念,直接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完成, 鎖就釋放了
      2. 要阻止另一個select , 則要手工加鎖, select 默認(rèn)是共享鎖, select之間的共享鎖是不沖突的, 所以, 如果只是共享鎖, 即使鎖沒有釋放, 另一個select一樣可以下共享鎖, 從而select出數(shù)據(jù) 

      BEGIN TRAN
      SELECT * FROM Table WITH(UPDLOCK) 
      --或者 SELECT * FROM Table WITH(TABLOCKX, READPAST) 具體情況而定。
      UPDATE ....
      COMMIT TRAN

      所有Select加 With (NoLock)解決阻塞死鎖,在查詢語句中使用 NOLOCK 和 READPAST 
      處理一個數(shù)據(jù)庫死鎖的異常時候,其中一個建議就是使用 NOLOCK 或者 READPAST 。有關(guān) NOLOCK 和 READPAST的一些技術(shù)知識點(diǎn): 
      對于非銀行等嚴(yán)格要求事務(wù)的行業(yè),搜索記錄中出現(xiàn)或者不出現(xiàn)某條記錄,都是在可容忍范圍內(nèi),所以碰到死鎖,應(yīng)該首先考慮,我們業(yè)務(wù)邏輯是否能容忍出現(xiàn)或者不出現(xiàn)某些記錄,而不是尋求對雙方都加鎖條件下如何解鎖的問題。 
      NOLOCK 和 READPAST 都是處理查詢、插入、刪除等操作時候,如何應(yīng)對鎖住的數(shù)據(jù)記錄。但是這時候一定要注意NOLOCK 和 READPAST的局限性,確認(rèn)你的業(yè)務(wù)邏輯可以容忍這些記錄的出現(xiàn)或者不出現(xiàn): 
      簡單來說:

      1.NOLOCK 可能把沒有提交事務(wù)的數(shù)據(jù)也顯示出來
      2.READPAST 會把被鎖住的行不顯示出來

      不使用 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。 
      NOLOCK 不要發(fā)出共享鎖,并且不要提供排它鎖。當(dāng)此選項生效時,可能會讀取未提交的事務(wù)或一組在讀取中間回滾的頁面。有可能發(fā)生臟讀。僅應(yīng)用于 SELECT 語句。 
      PAGLOCK:在通常使用單個表鎖的地方采用頁鎖。 
      READCOMMITTED:用與運(yùn)行在提交讀隔離級別的事務(wù)相同的鎖語義執(zhí)行掃描。默認(rèn)情況下,SQL Server 2000在此隔離級別上操作。 
      READPAST:跳過鎖定行。此選項導(dǎo)致事務(wù)跳過由其它事務(wù)鎖定的行(這些行平常會顯示在結(jié)果集內(nèi)),而不是阻塞該事務(wù),使其等待其它事務(wù)釋放在這些行上的鎖。 READPAST 鎖提示僅適用于運(yùn)行在提交讀隔離級別的事務(wù),并且只在行級鎖之后讀取。僅適用于 SELECT 語句。 
      READUNCOMMITTED:等同于 NOLOCK。 
      REPEATABLEREAD:用與運(yùn)行在可重復(fù)讀隔離級別的事務(wù)相同的鎖語義執(zhí)行掃描。 
      ROWLOCK:使用行級鎖,而不使用粒度更粗的頁級鎖和表級鎖。 
      SERIALIZABLE:用與運(yùn)行在可串行讀隔離級別的事務(wù)相同的鎖語義執(zhí)行掃描。等同于 HOLDLOCK。 
      TABLOCK:使用表鎖代替粒度更細(xì)的行級鎖或頁級鎖。在語句結(jié)束前,SQL Server 一直持有該鎖。但是,如果同時指定 HOLDLOCK,那么在事務(wù)結(jié)束之前,鎖將被一直持有。 
      TABLOCKX 使用表的排它鎖。該鎖可以防止其它事務(wù)讀取或更新表,并在語句或事務(wù)結(jié)束前一直持有。 
      UPDLOCK:讀取表時使用更新鎖,而不使用共享鎖,并將鎖一直保留到語句或事務(wù)的結(jié)束。UPDLOCK:的優(yōu)點(diǎn)是允許您讀取數(shù)據(jù)(不阻塞其它事務(wù))并在以后更新數(shù)據(jù),同時確保自從上次讀取數(shù)據(jù)后數(shù)據(jù)沒有被更改。 
      XLOCK:使用排它鎖并一直保持到由語句處理的所有數(shù)據(jù)上的事務(wù)結(jié)束時??梢允褂?span lang="EN-US"> PAGLOCK 或 TABLOCK 指定該鎖,這種情況下排它鎖適用于適當(dāng)級別的粒度。

       

      實(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)的不允許。

      復(fù)制代碼
      ------------------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' --需要等待解鎖
      復(fù)制代碼

      更新鎖:

      結(jié)論:“表鎖”鎖定對該表的Select、Update、Delete操作,但不影響對該表的Insert操作也不影響以主鍵Id為條件的Select

      復(fù)制代碼
      -----------------------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 --需要等待解鎖
      復(fù)制代碼

      主鍵鎖:

      結(jié)論:“行鎖+表鎖” 鎖定對該表的Select、Update、Delete操作,但不影響對該表的Insert操作也不影響以主鍵Id為條件的Select、Update、Delete

      復(fù)制代碼
      ------------------------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ù)主鍵可以)
      復(fù)制代碼

      索引鎖:

      結(jié)論:“行鎖+表鎖” 鎖定對該表的Select、Update、Delete操作,但不影響對該表的Insert操作也不影響以主鍵Id為條件的Select、Update、Delete,也不影響以索引列Name為條件的Update、Delete但不可以Select

      復(fù)制代碼
      ------------------------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ù)索引可以)
      復(fù)制代碼

      悲觀鎖(更新鎖-人工手動設(shè)置上鎖):

      結(jié)論:可以理解為在使用版本控制軟件的時候A遷出了一個文件,并且將這個文件鎖定,B就無法再遷出該文件了,直到A遷入解鎖后才能被其他人遷出。

      復(fù)制代碼
      ------------------------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' --需要等待解鎖(非主鍵不可)
      復(fù)制代碼

      樂觀鎖(人工通過邏輯在數(shù)據(jù)庫中模擬鎖)

      結(jié)論:可以理解為同樣在使用版本控制軟件的時候A遷出了一個文件,B也可以遷出該文件,兩個人都可以對此文件進(jìn)行修改,其中一個人先進(jìn)行提交的時候,版本并沒有變化所以可以正常提交,另一個后提交的時候,發(fā)現(xiàn)版本增加不對稱了,就提示沖突由用戶來選擇如何進(jìn)行合并再重新進(jìn)行提交。

      復(fù)制代碼
      --------------------------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),所以在此不放相同代碼了。
      復(fù)制代碼

      在樂觀鎖和悲觀鎖之間進(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)該使用悲觀鎖。

       

        本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報。
        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多