鎖的阻塞,死鎖,鎖公升級

2022-03-11 20:39:57 字數 3063 閱讀 3875

因為不同鎖之間的相容性關係,所以在有些時刻,乙個事務中的鎖需要等待另乙個事務中的鎖釋放它所占用的資源。在innodb儲存引擎的源**中,用mutex資料結構來實現鎖。在訪問資源前需要用mutex_enter函式進行申請,在資源訪問或修改完畢後立即執行mutex_exit函式

。當乙個資源已被乙個事務占有時,另乙個事務執行mutex_enter函式會發生等待,這就是阻塞。阻塞並不是一件壞事,阻塞是為了保證事務可以併發並且正常執行。

在innodb儲存引擎中,引數innodb_lock_wait_timeout用來控制等待的時間(預設是50秒),innodb_rollback_on_timeout用來設定是否在等待超時時對進行中的事務進行回滾操作(預設是off,代表不回滾)。

引數innodb_lock_wait_timeout是動態的,可以在mysql資料庫執行時進行調整,而innodb_rollback_on_timeout是靜態的,不可在啟動時進行修改,如:

set @@innodb_lock_wait_timeout=60;

set @@innodb_rollback_on_timeout=on;

error 1238(hy000):variable'innodb_rollback_on_timeout'is a read only variable

當發生超時時,資料庫會丟擲乙個1205的錯誤,如:

begin;

select * from t where a=1 for update;

error 1205(hy000):lock wait timeout exceeded;try restarting transaction

需要牢記的是,預設情況下innodb儲存引擎不會回滾超時引發的錯誤異常。其實innodb儲存引擎在大部分情況下都不會對異常進行回滾。

如在乙個會話中執行了如下語句:

#會話a

select * from t;

begin;

select * from t where a<4 for update;

會話a中開啟了乙個事務,next-key lock演算法下鎖定了小於4的所有記錄(其實也鎖定了4這個記錄)。在另乙個會話中執行如下語句:

#會話b

begin;

insert into t select 5;

insert into t select 3;

error 1205(hy000):lock wait timeout exceeded;try restarting transaction

select * from t;

可以看到,在會話b中插入記錄5是可以的,但是插入記錄3的話,因為next-key lock演算法的關係,需要等待會話a中事務釋放這個資源,因此等待後產生了超時。但是在超時後,我們再進行select會發現,5這個記錄並沒有並回滾。其實這時事務發生了錯誤,但是既沒有commit,也沒有rollback,這是十分危險的,使用者必須判斷是需要commit還是需要rollback,然後再進行下一步操作。

如果程式是序列的,那麼不可能發生死鎖。死鎖只發生於併發的情況,資料庫就是乙個併發進行著的程式,因此可能會發生死鎖。innodb儲存引擎有乙個後台的鎖監控執行緒

,該執行緒負責檢視可能的死鎖問題,並自動告知使用者。

下面的操作演示了死鎖的一種經典的情況,即a等待b,b在等待a:

(for update加的是排他鎖)在上述操作中,會話中的事務丟擲了1213這個出錯提示,即發生了死鎖。死鎖的原因是會話a和b的資源互相在等待。大多數的死鎖innodb儲存自己可以偵測到,不需要人為進行干預。

但是在上面的例子中,會話b中的事務丟擲死鎖異常後,會話a中馬上得到了記錄為2的這個資源,這其實是因為會話b中的事務發生了回滾,否則會話a中的事務是不可能得到該資源的。innodb儲存引擎並不會回滾大部分的錯誤異常,但是死鎖除外。發現死鎖後,innodb儲存引擎會馬上回滾乙個事務,這點是需要注意的。如果在應用程式中捕獲了1213這個錯誤,其實並不需要對其進行回滾。

oracle資料庫中產生死鎖的常見原因是沒有對外鍵新增索引,而innodb儲存引擎會自動對其進行新增,因此很好地避免了這種情況的發生。人為刪除外來鍵上的索引資料庫會丟擲乙個異常:

create table p(a int,primary key(a));

create table c(b int,foreign key(b) references p(a))engine=innodb;

show index from c\g;

drop index b on c;

error 1553(hy000):cannot drop index 'b':needed in a foreign key constraint

可以看到,雖然在建立子表時指定了外來鍵,但是innodb儲存引擎還是自動在外鍵列上建立了乙個索引b,而人為刪除這個列卻是不允許的。

鎖公升級(lock escalation)是指將當前鎖的粒度降低。舉例來說,資料庫可以把乙個表的1 000個行鎖公升級為乙個頁鎖,或者將頁鎖公升級為表鎖。如果資料庫的設計中認為鎖是一種稀有資源,而且想避免鎖的開銷,那資料庫中會頻繁出現鎖公升級現象。

microsoft sql server資料庫的設計認為鎖是一種稀有的資源,在適合的時候會自動地將行、鍵或者分頁級鎖公升級為更粗粒度的表級鎖。這種公升級保護了系統資源,防止系統使用太多的記憶體來維護鎖,從一定程度上提高了效率。

即使在microsoft sql server 2005的版本之後,sql server資料庫支援了行鎖,但是其設計和innodb儲存引擎完全不同,在以下情況下依然可能發生鎖公升級:

由一句單獨的sql語句在乙個物件上持有的鎖數量超過了閾值,預設的這個閾值為5000。值得注意的是,如果是不同物件的話,則不會發生鎖公升級。

鎖資源占用的記憶體超過了啟用記憶體的40%時,就會發生鎖公升級。

在microsoft sql server資料庫中,因為鎖是一種稀有的資源,因此鎖公升級會帶來一定的效率提高。但是鎖公升級帶來的乙個問題卻是,因為鎖粒度的降低而導致併發效能的降低。

innodb儲存引擎不存在鎖公升級的問題。在innodb儲存引擎中,1個鎖的開銷與1 000 000個鎖是一樣的,都沒有開銷。這一點和oracle資料庫比較類似。

鎖 互斥鎖,死鎖

當多個執行緒幾乎同時修改某乙個共享資料的時候,需要進行同步控制 執行緒同步能夠保證多個執行緒安全訪問競爭資源,最簡單的同步機制是引入互斥鎖。互斥鎖為資源引入乙個狀態 鎖定 非鎖定 某個執行緒要更改共享資料時,先將其鎖定,此時資源的狀態為 鎖定 其他執行緒不能更改 直到該執行緒釋放資源,將資源的狀態變...

處理鎖 阻塞和死鎖(2) 偵測阻塞和阻塞查詢

如果乙個事務正在等待一些給其他事務鎖定的資源。這個事務就被成為 被阻塞的事務 反過來,引起阻塞的事務,也就是鎖定資源並造成其他事務等待的事務叫做 正在阻塞的事務 長時間執行事務會阻塞其他事務和查詢,使他們等待長時間。在繁重的系統中,很多時候我們會遇到阻塞問題,如果乙個事務因為阻塞未完成。會造成一些列...

3 全域性鎖 表鎖 行鎖,死鎖

對整個資料庫例項加鎖。mysql提供加全域性讀鎖的方法 flush tables with read lock ftwrl 這個命令可以使整個庫處於唯讀狀態。使用該命令之後,資料更新語句 資料定義語句和更新類事務的提交語句等操作都會被阻塞。可以用unlock tables主動釋放鎖 1.如果在主庫備...