一、前言
死鎖,在我們的項目中有發生過,但不頻繁;但是因為鎖處理的不好,不能合理地規划鎖,導致性能下降是經常發生的。通過本文章,除了了解如何避免死鎖外,更多的內容是如何使用鎖。本文不會講的很細,有的地方也不是很嚴謹,但是不影響對內容的理解。更細節和高級的知識,可以百度。
二、什么是鎖
我們這里講的鎖,是數據庫的鎖(lock)。當數據庫要對某個表,或者某條數據修改時,會首先將其加鎖,防止其他進程或事務修改它,以免產生不可預期的結果。關於鎖的定義,鎖的類型,大家可以搜索。鎖的內容(或者稱為對象,比如表,表的記錄),一般稱之為資源。
鎖一般有兩個級別:行級鎖和表級鎖。前者的鎖定范圍是表記錄,后者的范圍是整表。顯然,后者的范圍更大。
三、何時會發生“鎖”
當執行update語句時,數據庫會對修改范圍內的表記錄加鎖;
當你修改表的結構時,比如 alter table…,數據庫會對表加鎖
當你執行select 1 from bsuser for update 時,會對整表加鎖。這種語法俗稱“顯式加鎖”。
鎖的有效期會從加鎖開始直到事務結束(提交或者回滾)。
我不知道有沒有主動釋放鎖的語法,感興趣的可以查一查。
四、什么是死鎖
鎖不可怕,在系統運行期間,數據庫不停地產生鎖,釋放鎖,是很平常的事情,但是可怕的是死鎖(dead lock),發生死鎖,會導致多個進程阻塞等待。這時候,數據庫會提示“系統在申請資源時,檢測到死鎖”類似的提示信息。死鎖就像十字路口塞車一樣,四個方向都走不了。
死鎖發生有四個必要條件,我只講最后一個,也是最關鍵的,就是兩個事務互相申請被對方持有的資源。舉例:進程(事務)A持有資源R1,進程(事務)B持有資源R2,現在進程A申請R2,進程B申請R1。就是兩個進程互相申請被對方加鎖(持有)的資源,且互不相讓,死鎖發生了。
發生死鎖后,數據庫會在等待一段時間后,根據一定的原則,犧牲掉一個進程,以避免死鎖持續下去。
五、鎖的危害
數據庫鎖是保證數據數據正確被修改的必不可少的手段,是關系型數據庫很重要的一個保證數據完整性的工具。這里不說鎖的優點,單說其缺點,然后研究怎么規避這些缺點,降低鎖的負面影響。
1、 降低系統性能。一般資源被進程鎖定,其他要申請該資源的進程就要等待,直到資源被前一個進程釋放,這個等待過程就是系統性能降低的過程。等待的越久,系統性能下降的約厲害。
2、 導致死鎖。死鎖會導致更長時間的進程等待,而且這種等待是無解的,只有通過外部力量的干涉才能解鎖,比如數據庫介入,犧牲掉一個進程。這樣這個進程的操作就會被回滾。
鎖等待或者死鎖對用戶的直接影響是,如果用戶的一個界面操作導致的數據庫操作被鎖阻塞,該處理進程處於等待狀態,那用戶的操作就會被凍結,不會得到響應。用戶的話說就是“界面卡了”。這種情況如果多了,卡的時間長了,會導致用戶的體驗急劇下降,如果出現死鎖,還會導致用戶所做的修改不能更新到數據庫中,被回滾。
六、如何降低鎖的影響,避免死鎖
我們在實踐中,要了解鎖發生的原因,鎖的機制,正確編程,合理使用鎖機制。
1、 避免“鎖”
有時候,必須通過鎖確保程序執行完美。但是凡事都要有代價。有時候我們需要仔細將加鎖帶來的副作用和帶來的好處做個對比。如果帶來的壞處遠遠大於帶來的好處,或者換句話說,如果帶來的好處非常小,我們就可以不使用鎖。我舉個例子。在對考核計划增加考核對象時,要檢查不能加入重復的考核對象。為了完美實現這個業務邏輯,需要對考核計划加鎖,使得在增加對象時,其他程序都不可以增加對象。這樣做完美地實現了這個業務邏輯,但是由於是對整表加鎖,大大增加了鎖沖突的可能,而且,如果不這樣做的話,根據用戶的使用場景分析,考核對象重復增加的可能性也極低。這種情況下,就可以不使用鎖。為了避免鎖沖突(這種大的副作用),我可以容忍在某種極端情況下導致考核對象重復的情況發生(極小概率出現的副作用)。但是后面我會給出一個方案,來確保程序完美的前提下,如何縮小鎖的粒度(不鎖表)。
2、 盡量縮短“鎖”的時間
使用鎖的一個原則就是 盡可能晚地加鎖,盡可能早地釋放鎖。也就是說,要合理規划處理邏輯,只在必須的最后時刻才執行update語句(加鎖),並盡可能早提交或回滾事務(釋放鎖)。一個錯誤的例子就是:
(1)update ………………………
(2)復雜的耗時的java代碼
(3)commit
如果可能話,就把 (1)和(2)調換下順序,推遲加鎖的時間。
縮短鎖的時間,不能完全避免沖突,但是可以降低發生沖突的概率。就行在大街上走,走的快一些,盡量減少在大街上行走的時間,可以降低被熟人看見概率是一個道理。
3、 減小鎖的粒度
只對必要的資源加鎖,不擴大鎖的范圍。
select 1 from 表 for update這種鎖全表要堅決避免,如果要鎖指定的行,加上where限定條件。
和2一樣,減小鎖的粒度,也能降低發生鎖沖突的概率。
4、 避免死鎖。
前面說過,死鎖發生的根本原因就是不合理的資源申請順序,如果多個程序都要使用相同的某幾個資源,但是申請順序不一致,就可能導致死鎖發生。因此,我們要約定一個確定資源申請順序的一個標准,大家都來遵循它。
1) 按照資源id的大小順序(比如我要鎖定多條員工記錄,我按照員工號的排序從大到小鎖定)
2) 對有層級關系的資源,按照層級從高到低的順序申請。比如”單據頭->單據明細行”的順序
3)按照資源名稱排序從大到小進行申請。比如按照表的名稱排序。
七、后記
在實踐中,對鎖的認識和管理,許多新人缺乏這方面的概念和意識,沒有意識到合理使用鎖的重要性。但是鎖對系統的性能有很大影響。合理使用鎖的三個關鍵,1是減少資源的占用時間,2是,減少鎖的粒度 ,3,絕對要避免死鎖。