MySQL技術內幕 InnoDB儲存引擎:阻塞、死鎖、鎖升級
1、堵塞
因為不同鎖之間的相容性關係,在有些時刻一個事務中的鎖需要等待另外一個事務中的鎖釋放它所佔用的資源,這就是堵塞。
- 引數innodb_lock_wait_timeout用來控制等待的時間,預設50秒,是可以動態設定的。
- 引數innodb_rollback_on_timeout用來設定是否在等待超時時對進行中的事務進行回滾操作。預設是OFF。(靜態引數,無法是mysql執行時修改)
在預設情況下InnoDB儲存引擎不會回滾超時引發的錯誤異常。比如一個事務中執行兩條insert 語句,第一條正常執行,第二條出現超時異常,這時innodb不會回滾整個事務,即第一條insert語句,已經提交到資料庫。
2、死鎖
死鎖是指兩個或兩個以上的事務在執行過程中,因爭奪鎖資源而造成的一種互相等待現象。
解決死鎖問題最簡單方式是不要有等待,將任何的等待都轉化為回滾,並且事務重新開始。毫無疑問,這的確可以避免死鎖問題的產生。但是效能很差。
解決死鎖問題最簡單方法是超時,即當兩個事務互相等待時,當一個等待時間超過閾值時,其中一個事務進行回滾,另外一個事務就能繼續進行。雖然簡單,但是僅僅根據FIFO進行回滾,若超時的事務佔據權重比較大,會浪費較多時間。
當前資料庫都普遍採用wait-for graph(等待圖)的方式來進行死鎖檢測。可以發現wait-for graph是一種較為主動的死鎖檢測機制,在每個事務請求鎖併發生等待時都會判斷是否存在迴路,若存在則有死鎖,通常來說,InnoDB儲存引擎會選擇回滾undo量最小的事務
InnoDB儲存引擎並不會回滾大部分的錯誤異常,但是死鎖除外。發現死鎖後,InnoDB儲存引擎會馬上回滾一個事務,這點是需要注意的。如果在應用程式中捕獲了1213這個錯誤,其實並不需要對其進行回滾。
3、鎖升級
鎖升級(Lock Escalation)是指將當前鎖的粒度降低。舉例來說,資料庫可以把一個表的1 000個行鎖升級為一個頁鎖,或者將頁鎖升級為表鎖。如果資料庫的設計中認為鎖是一種稀有資源,而且想避免鎖的開銷,那資料庫中會頻繁出現鎖升級現象。
Microsoft SQL Server資料庫的設計認為鎖是一種稀有的資源,在適合的時候會自動地將行、鍵或者分頁級鎖升級為更粗粒度的表級鎖。這種升級保護了系統資源,防止系統使用太多的記憶體來維護鎖,從一定程度上提高了效率。
即使在Microsoft SQL Server 2005的版本之後,SQL Server資料庫支援了行鎖,但是其設計和InnoDB儲存引擎完全不同,在以下情況下依然可能發生鎖升級:
- 由一句單獨的SQL語句在一個物件上持有的鎖數量超過了閾值,預設的這個閾值為5000。值得注意的是,如果是不同物件的話,則不會發生鎖升級。
- 鎖資源佔用的記憶體超過了啟用記憶體的40%時,就會發生鎖升級。
在Microsoft SQL Server資料庫中,由於鎖是一種稀有的資源,因此鎖升級會帶來一定的效率提髙。但是鎖升級帶來的一個問題卻是因為鎖粒度的降低而導致併發效能的降低。
IrnioDB儲存引擎不存在鎖升級的問題。因為其不是根據每個記錄來產生行鎖的,相反,其根據每個事務訪問的每個頁對鎖進行管理的,採用的是點陣圖的方式。因此不管一個事務鎖住頁中一個記錄還是多個記錄,其開銷通常都是一致的。
假設一張表有3 000 000個數據頁,每個頁大約有100條記錄,那麼總共有300 000 000條記錄。若有一個事務執行全表更新的SQL語句,則需要對所有記錄加X鎖。若根據每行記錄產生鎖物件進行加鎖,並且每個鎖佔用10位元組,則僅對鎖管理就需要差不多需要3GB的記憶體。而IxmoDB儲存引擎根據頁進行加鎖,並採用點陣圖方式,假設每個頁儲存的鎖資訊佔用30個位元組,則鎖物件僅需90MB的記憶體。由此可見兩者對於鎖資源開銷的差距之大。
本文整理自:《MySQL技術內幕 InnoDB儲存引擎》
個人微信公眾號:
作者:jiankunking 出處:http://blog.csdn.net/jiankunking