1.數據庫鎖的種類
① 共享鎖
共享鎖是在執行select操作時使用的鎖機制.
共享鎖與共享鎖共存,即當一個事務正在對A表進行查詢操作時,另一個事務同樣可以對A表進行查詢操作,演示如下:
1 T1: select * from A;(加共享鎖A) 2 T2: select * from A;(加共享鎖B) 3 -- 此時T1,T2可同時執行
② 排它鎖
排它鎖是在執行update,delete等對數據有修改操作時使用的鎖.
排它鎖不與排它鎖以及共享鎖共存,即當一個事務正在對A表進行更新操作時,會對操作的數據加上排它鎖,其他事務無法訪問更無法修改被加鎖的數據行,演示如下:
1 T1: update A set a=1 where id<100; 2 T2: update A set a=2 where id>100; 3 T3: update A set a=3 where id=50; 4 T4: select * from A where id<50;
當T1先到達時,會對id<100的數據加上排它鎖,其他事務無法訪問這些數據,由於T2訪問的是id>100的數據,改數據未被加上排它鎖,所以T2能夠對id>100的數據加排它鎖,
但是T3訪問的數據已經被T1加上排它鎖,除非T1執行完畢釋放排它鎖,T3才能訪問id=50的數據.T4跟T3同理.
排它鎖的並發性低下,請看下面這個例子:
1 T1:update table set column1='hello' where id=10 2 T2:update table set column1='world' where id=20
該例子有兩種情況:
第一種情況:如果id是主鍵(有主鍵索引)或者有索引的一列,T1會一下子找到id=10的那一列,並對該列加上排它鎖.T2同理,這兩個事務互不干涉,能夠並發執行
第二種情況:如果id是普通的列,且沒有索引,那么當T1搜索到id=10這一列並對他加上排它鎖之后,由於T2需要全表掃描找到id=20這一列,但是由於T1對id=10這一列加上了排它鎖,T2無法訪問,就會進入阻塞狀態,試想如果T1是一個較大的事務,那么T2一直得不到想要的資源,嚴重影響用戶體驗,尤其是在web應用這種對程序的效率要求較高的應用.
排它鎖的並發性低下,但是更新鎖能夠比較好的解決這個問題.
③ 更新鎖
被加上更新鎖的數據是可能被修改的數據.更新鎖與排它鎖及更新鎖互斥,與共享鎖共存.演示如下:
1 T1: select * from A;(加更新鎖) 2 T2: select * from A;(加更新鎖) 3 T3: select * from A;(加共享鎖) 4 T4: update A set a=1;(加排它鎖)
當T1到達時,對A表加更新鎖,然后T2到達,企圖對A表加更新鎖,但是發現表A已經被加上更新鎖,其阻塞.然后T3到達,對A表加共享鎖,由於共享鎖與更新鎖共存,所以T3能夠加上共享鎖並能夠正常讀取,然后T4到達,由於排它鎖與更新鎖互斥,所以T4阻塞.以下是另一個例子:
1 T1: 2 select * from A;(加更新鎖) 3 update set a=1 where id=1; 4 T2: 5 select * from A;(加共享鎖) 6 update set a=1 where id=1;
T1先到達執行查詢操作,同時T2到達,T2開始查詢操作,兩者可以共存.
第一種情況:假設T2查詢操作先結束,准備執行更新操作對相應行上鎖時,發現該表有了更新鎖,所以該操作阻塞.當T1查詢操作結束后,執行更新操作,更新鎖自動升級為排它鎖,T1更新結束之后,釋放鎖,T2開始更新.
第二種情況:假設T1查詢操作先結束,准備執行更新操作,更新鎖升級為排它鎖,由於排它鎖與共享鎖不同存,T2的查詢操作被阻塞,等T1的更新操作結束之后,T2才能繼續操作.
④ 意向鎖
意向鎖就是反應當前表是否有鎖的鎖機制.
如果T1正在對A表的一部分數據進行更新操作(加了行級鎖),這個時候另一個事務T2需要對A表加表級鎖,由於表如果存在行級鎖之后無法再添加表級鎖,它會對全表進行掃描判斷表的每一行是否存在排它鎖,這樣效率低下.意向鎖就會能夠防止這種情況的發生,當A表有一行被加上了排它鎖之后,A表會自動被加上意向排它鎖,那么其他事務需要加表級鎖的事務只需要判斷A表是否有意向鎖就行了.
1 T1: update A set a=1 where id=1;(加行級排它鎖) 2 T2: update A set a=1 where id=1;(加表級排它鎖)
當T1執行更新操作時,對id=1這一行加上行級鎖之后,數據庫會自動對A表加上一個意向排它鎖,當T2到達之后,T2會判斷A表是否有意向排它鎖,如果存在則阻塞,不存在則對A表加上表級排他鎖.
⑤ 小tips
在操作數據庫時,數據庫會自動為操作的數據添加合適的鎖,這就是為什么不需要懂得數據庫的鎖機制我們依然可以愉快的寫SQL.不同的存儲引擎支持的鎖機制不同,根據需要可選擇不同的存儲引擎.
程序員可手動對對象加鎖,這里以我熟悉的mysql數據庫為例:這里我使用的是mysql默認的innodb存儲引擎,該引擎支持行級鎖,表級鎖.InnoDB行鎖是通過給索引上的索引項加鎖來實現的,如果不存在索引,則對整張表加鎖.
添加表級鎖如下:
1 LOCK table admin write; -- 對admin加表鎖 2 unlock tables;-- 釋放當前session具有的表鎖
對表添加了寫鎖之后,是無法對其他表進行操作的,所有事務必須在一開始獲取自己需要的所有鎖,獲取了寫鎖之后能進行寫與讀操作,獲取了讀鎖之后,只能進行讀操作
添加行級鎖如下:
1 select * from admin for update; -- 獲得排它鎖
更多關於mysql的鎖機制請自行看官方文檔.
2.樂觀鎖與悲觀鎖
樂觀鎖與悲觀鎖是為了保持事務的隔離性以及數據庫的一致性而被業內定義出來的一種手段,並不屬於數據庫鎖機制,但是悲觀鎖是依賴於數據庫的鎖機制實現的.
① 樂觀鎖
在關系數據庫管理系統里,樂觀並發控制(又名“樂觀鎖”,Optimistic Concurrency Control,縮寫“OCC”)是一種並發控制的方法。它假設多用戶並發的事務在處理時不會彼此互相影響,各事務能夠在不產生鎖的情況下處理各自影響的那部分數據。在提交數據更新之前,每個事務會先檢查在該事務讀取數據后,有沒有其他事務又修改了該數據。如果其他事務有更新的話,正在提交的事務會進行回滾。
樂觀鎖認為其他事務都不會修改自己的正在操作的數據,所以不會使用數據鎖機制(數據庫鎖機制無法關閉,這里說的不使用是不會像悲觀鎖那樣顯示加鎖,悲觀鎖操作見第4點)..
② 悲觀鎖
在關系數據庫管理系統里,悲觀並發控制(又名“悲觀鎖”,Pessimistic Concurrency Control,縮寫“PCC”)是一種並發控制的方法。它可以阻止一個事務以影響其他用戶的方式來修改數據。如果一個事務執行的操作都某行數據應用了鎖,那只有當這個事務把鎖釋放,其他事務才能夠執行與該鎖沖突的操作。
悲觀並發控制主要用於數據爭用激烈的環境,以及發生並發沖突時使用鎖保護數據的成本要低於回滾事務的成本的環境中。
悲觀鎖的實現主要依賴於數據庫的鎖機制,會顯示的使用鎖.
3.樂觀鎖並發控制
樂觀鎖並發控制常用方法是使用一個版本號控制手段.即在表中添加一個版本號version字段,當有一個事務對該數據進行操作時會取得版本號,當事務對數據更新時會判斷數據庫中的version是否跟自己取得version一致,一致則說明該數據未被修改過,否則說明數據被修改過,更新操作失敗.更新成功則對version字段加一.
T1: select * from A; update A set a=1,version=version+1 where id=1 and version=vs; T2: select * from A; update A set a=2,version=version+1 where id=1 and version=vs; -- vs為事務取得的版本號
假設T1,T2並發的對A表進行操作,T1,T2取得的version字段值相同,T1事務先執行完,T1執行完之后version被加一,T2在提交更新時,因為version字段已經被修改,所以更新失敗.
樂觀鎖不適用於更新操作較多的情況,如果更新操作較多,會出現很多更新失敗的情況,需要大量回滾,重新操作,程序效率大幅度降低.但是他的查詢並發度高.
4.悲觀鎖並發控制
悲觀鎖並發控制主要通過對操作的數據加鎖實現.下面這個例子是通過加排它鎖實現:
select * from A where id=1 for update
在操作A表示,對id=1的行加上行級鎖,其他事務無法操作該數據.實現了悲觀鎖.
悲觀鎖並發度低,每次都需要對表上鎖,但是能夠保證數據的安全.