因為不同鎖之間的兼容性關系,在有些時刻一個事務中的鎖需要等待另一個事務中的鎖釋放它占有的資源,這就是阻塞。阻塞不是一件壞事,是為了保證事務可以並發並且正常的運行
在InnoDB存儲引擎中,參數innodb_lock_wait_timeout用來控制等待的時間(默認50秒),innodb_rollback_on_timeout用來設定是否在等待超時時對進行中的事務進行回滾操作(默認為OFF,代表不會滾)參數innodb_lock_wait_timeout是動態的,可以在運行中調整
>set @@innodb_lock_wait_timeout=60;
而innodb_rollback_on_timeout是靜態的,不可在啟動時進行修改
當發生超時,MySQL會拋出一個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
可以看到,在會話B中插入記錄5是可以的,但是插入記錄為3的時候,因為會話A中Next-Key Lock算法的關系,需要等待會話A中事務釋放這個資源,所以等待后產生超時,但是在超時后用戶再進行SELECT操作時發現,5這個記錄依然存在
這是因為會話B中事務雖然拋出異常,但是既沒有進行COMMIT操作,也沒有進行ROLLBACK。而這是非常危險的狀態,因此用戶必須判斷是否需要COMMIT還是ROLLBACK,之后在進行下一步操作