前言:為了保證資料的完整性和一緻性,資料庫系統采用鎖來實作事務的隔離性。各種大型資料庫采用的鎖基本理論是一緻的,但在具體實作上各有差别。從并發事務鎖定的關系上看,可以分為共享鎖定和獨占鎖定。從鎖定的對象不同,一般可以分為表鎖定和行鎖定。
鎖分為悲觀鎖和樂觀鎖:
● 悲觀鎖:悲觀的思想,認為并發問題總會出現,每次一個事務讀取某一條記錄後,就會把這條記錄鎖住,這樣其它的事務要想更新,必須等以前的事務送出或者復原解除鎖。悲觀鎖的性能低,其基本種類有:
● 樂觀鎖:樂觀的思想,認為并發問題不總是出現;每次送出一個事務更新時,我們先看看要修改的東西從上次讀取以後有沒有被修改過,如果修改過,那麼更新就會失敗。樂觀鎖其實并不會鎖定任何記錄,是以如果我們資料庫的事務隔離級别設定為讀取已送出或者更低的隔離界别,那麼是不能避免不可重複讀問題的(因為此時讀事務不會阻塞其它事務),是以采用樂觀鎖的時候,系統應該要容許不可重複讀問題的出現。
共享鎖用于讀取資料操作,它是非獨占的,允許其他事務同時讀取其鎖定的資源,但不允許其他事務更新它。
● 加鎖的條件:當一個事務執行select語句時,資料庫系統會為這個事務配置設定一把共享鎖,來鎖定被查詢的資料。
● 解鎖的條件:在預設情況下,資料被讀取後,資料庫系統立即解除共享鎖。例如,當一個事務執行查詢“select
* from accounts”語句時,資料庫系統首先鎖定第一行,讀取之後,解除對第一行的鎖定,然後鎖定第二行。這樣,在一個事務讀操作過程中,允許其他事務同時更新accounts表中未鎖定的行。
● 與其他鎖的相容性:如果資料資源上放置了共享鎖,還能再放置共享鎖和更新鎖。
● 并發性能:具有良好的并發性能,當資料被放置共享鎖後,還可以再放置共享鎖或更新鎖。是以并發性能很好。
更新鎖在的初始化階段用來鎖定可能要被修改的資源,這可以避免使用共享鎖造成的死鎖現象。例如,對于以下的update語句:
更新操作需要分兩步:
讀取accounts表中id為1的記錄;
執行更新操作。
如果在第一步使用共享鎖,再第二步把鎖更新為獨占鎖,就可能出現死鎖現象。例如:兩個事務都擷取了同一資料資源的共享鎖,然後都要把鎖更新為獨占鎖,但需要等待另一個事務解除共享鎖才能更新為獨占鎖,這就造成了死鎖。
更新鎖有如下特征:
● 加鎖的條件:當一個事務執行update語句時,資料庫系統會先為事務配置設定一把更新鎖。
● 解鎖的條件:當讀取資料完畢,執行更新操作時,會把更新鎖更新為獨占鎖。
● 與其他鎖的相容性:更新鎖與共享鎖是相容的,也就是說,一個資源可以同時放置更新鎖和共享鎖,但是最多放置一把更新鎖。這樣,當多個事務更新相同的資料時,隻有一個事務能獲得更新鎖,然後再把更新鎖更新為獨占鎖,其他事務必須等到前一個事務結束後,才能擷取得更新鎖,這就避免了死鎖。
● 并發性能:允許多個事務同時讀鎖定的資源,但不允許其他事務修改它。
獨占鎖:獨占鎖也叫排他鎖,适用于修改資料的場合。它所鎖定的資源,其他事務不能讀取也不能修改。
● 加鎖的條件:當一個事務執行insert、update或delete語句時,資料庫系統會自動對sql語句操縱的資料資源使用獨占鎖。如果該資料資源已經有其他鎖(任何鎖)存在時,就無法對其再放置獨占鎖了。
● 解鎖的條件:獨占鎖需要等到事務結束才能被解除。
● 相容性:獨占鎖不能和其他鎖相容,如果資料資源上已經加了獨占鎖,就不能再放置其他的鎖了。同樣,如果資料資源上已經放置了其他鎖,那麼也就不能再放置獨占鎖了。
● 并發性能:不用說了,最差。隻允許一個事務通路鎖定的資料,如果其他事務也需要通路該資料,就必須等待,起到前一個事務結束,解除了獨占鎖,其他事務才有機會通路該資料。
當一個事務通路某種資料庫資源時,如果執行select語句,必須先獲得共享鎖,如果執行insert、update或delete語句,必須獲得獨占鎖,這些鎖用于鎖定被操作的資源。
當第二個事務也要通路相同的資源時,如果執行select語句,也必須先獲得共享鎖,如果執行insert、update或delete語句,也必須獲得獨占鎖。此時根據已經旋轉在資源上的鎖的類型,來決定第二個事務應該等待第一個事務解除對應資源的鎖定,還是可以立刻獲得鎖。
樂觀鎖與資料庫鎖機制無關,其鎖實作政策為:
● 版本 (version) 字段:在我們的實體中增加一個版本控制字段,每次事務更新後就将版本字段的值加 1。
● 時間戳 (timestamps):實作方式和版本字段差不多,同樣是在需要樂觀鎖控制的table中增加一個字段,名稱無所謂,字段類型使用時間戳(timestamp),和上面的version類似,也是在更新送出的時候檢查目前資料庫中資料的時間戳和自己更新前取到的時間戳進行對比,如果一緻則ok,否則就是版本沖突。
版本字段方式執行個體:
我們修改t_person表,添加version字段表示目前記錄的版本,預設值為1。
當事務查詢記錄時得到version=1,再執行update時需要比較目前version的值是否與之前查詢到的version相同,決定update是否執行成功。如果update成功,還要把version的值加1。
場景模拟:
事務1:查詢時得到version=1;
事務2:查詢時得到version=1;
事務1:執行update時因為version沒有改變,是以update執行成功,update不隻修改了age=42,還修改了version=2;
事務2:執行update語句時version已經為2,而查詢時的version為1,是以update執行失敗;
小結:樂觀鎖和悲觀鎖的差別在于是否認為并發問題一定會存在。悲觀鎖也就是上面講到的我們認為的通常意義上的鎖,而樂觀鎖實質是與資料庫鎖機制無關的。共享鎖定會防止獨占鎖定,但允許其它的共享鎖定。而獨占鎖定既防止其它的獨占鎖定,也防止其它的共享鎖定。為了更改資料,資料庫必須在進行更改的行上施加行獨占鎖定。