文章出處

一:鎖的概念

        按照寫技術博客的套路,應該對鎖的概念做一個介紹,我又想,能點擊進入本篇博客的同學,想必都是聽說過鎖的。所以我盡量用簡練的語言來表述一下。

        鎖的定義:鎖主要用于多用戶環境下,保證數據庫完整性和一致性的技術。

        鎖的解釋:當多個用戶并發地存取數據時,在數據庫中就會產生多個事務同時存取同一數據的情況。若對并發操作不加控制就可能會讀取和存儲不正確的數據,破壞數據庫的完整性和一致性。當事務在對某個數據對象進行操作前,先向系統發出請求,對其加鎖。加鎖后事務就對該數據對象有了一定的控制。

二:鎖的分類

        鎖的概念非常簡單,簡單的來用幾句話就能描述它的用途。但是鎖的分類,就明顯要復雜一些了。

        鎖的分類,在教材上,網絡上好多都是按兩個維度來描述的。一種維度是按鎖的功能來劃分,一種維度是按概念來劃分。09年的時候,我做了一個數據庫的培訓教程,把鎖的分類給截出來擺一下。

 

時隔了幾年,看起來PPT看起來很粗糙。與我這些PPT模板沒法比,但是內容仍然經典。

三:鎖的關鍵字

       共享鎖,排它鎖這樣的鎖,數據庫引擎會自動管理和優化,平時寫SQL的時候,很少有去關心鎖的關鍵字。

       但是今天是抱著學習的態度來看博客的,所以必須得把這幾個關鍵字都用一篇。

SELECT * FROM AppLog WITH (HOLDLOCK) /*共享鎖*/

SELECT * FROM AppLog WITH (UPDLOCK)  /*更新鎖*/ 

SELECT * FROM AppLog WITH (XLOCK) WHERE LogID='AA599A4E-B727-4A65-8010-00001661765E'; /*排它鎖*/  

SELECT * FROM AppLog WITH (ROWLOCK) WHERE LogID='6BE2C680-0C9F-43FA-9B4E-00000A6C1CEF'; /*行鎖*/ 

SELECT * FROM AppLog WITH (TABLOCKX) /*大容量更新鎖*/ 

SELECT * FROM AppLog WITH (XLOCK,ROWLOCK) WHERE LogID='AA599A4E-B727-4A65-8010-00001661765E'; /*鎖的組合使用*/ 

/*XLOCK 本身是鎖住數據行的,TABLOCKX是鎖住整張表*/

SELECT * FROM AppLog WITH (NOLOCK)   /*不加鎖,當一個事務回滾后,出現臟數據*/   

SELECT * FROM AppLog WITH (READPAST)  /*忽略掉加鎖的數據(行數據,頁數據)*/

四:死鎖的發生

比如現在的數據庫用兩個用戶在用,

用戶1:

BEGIN TRAN
SELECT * FROM AppLog WHERE LogID  = 'A10BA165-6E52-4AFB-9EA8-000000D6B90A';
UPDATE AppLog SET AppPostion = AppPostion + AppPostion WHERE LogID = 'A10BA165-6E52-4AFB-9EA8-000000D6B90A';

用戶2:

BEGIN TRAN
SELECT * FROM AppLog WHERE LogID  = 'A10BA165-6E52-4AFB-9EA8-000000D6B90A';
UPDATE AppLog SET AppPostion = AppPostion + AppPostion WHERE LogID = 'A10BA165-6E52-4AFB-9EA8-000000D6B90A';


比如用戶1,用戶2同時執行 SELECT,用戶1對記錄加了共享鎖,用戶2對記錄也加了共享鎖,當用戶1 SELECT 執行完畢,準備執行UPDATE的時候,根據鎖機制,用戶1的共享鎖需要升
級到排他鎖才能執行接下來的UPDATE.

在升級排他鎖前,必須等待記錄上的其它共享鎖釋放,但是因為共享鎖只有等事務結束后才釋放。因為用戶2的共享鎖不釋放而導致用戶1等(等用戶2釋放共享鎖,自己好升級成排他鎖),同理,這時也因為用戶1的共享鎖不釋放而導致用戶2等待。死鎖就發生了。

五:無鎖查詢技巧

打開兩個查詢窗口:其中一個執行下面語句:

CREATE TABLE a
(
    id INT ,
    name NVARCHAR(20)
) 
BEGIN TRAN
INSERT a VALUES ('1','a')--開啟一個事務,而不提交也不回滾,此時insert 語句產生的排它鎖是不會釋放的

在另一個窗口中執行:

select COUNT(*) from a with(nolock)--無鎖查詢,會查出結果為1

select COUNT(*) from a with(readpast)--忽略所有有鎖的記錄,此時為0

然后執行select * from a --此時是查不出結果的,會無限地等待下去,因為排它鎖未釋放,默認查詢的共享鎖與之不兼容,所以就一直等待排它鎖的釋放,才會返回結果,即使表中已有許多數據,而排它鎖只鎖了一條記錄,但是,查詢語句也要等待這一條記錄的鎖的釋放,才會返回結果。 這便是人工手動設置的因為排它鎖未釋放而導致的死鎖(不是相互等待,而是一方無盡的等待!)。

 

歡迎討論。

 


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()