MySQL探祕(五):InnoDB鎖的型別和狀態查詢
鎖是資料庫系統區分於檔案系統的一個關鍵特性。資料庫使用鎖來支援對共享資源進行併發訪問,提供資料的完整性和一致性。此外,資料庫事務的隔離性也是通過鎖實現的。InnoDB在此方面一直優於其他資料庫引擎。InnoDB會在行級別上對錶資料上鎖,而MyISAM只能在表級別上鎖,二者效能差異可想而知。
InnoDB儲存引擎中的鎖
InnoDB儲存引擎實現瞭如下兩種標準的行級鎖:
- 共享鎖(S Lock),允許事務讀取一行
- 排他鎖(X Lock),允許事務刪除或更新一行資料
如果一個事務T1已經獲取了行r的共享鎖,那麼另外一個事務T2可以立刻獲得行r的共享鎖,因為讀取並不會改變資料,可以進行併發的讀取操作;但若其他的事務T3想要獲取行r的排他鎖,則必須等待事務T1和T2釋放行r上的共享鎖之後才能繼續,因為獲取排他鎖一般是為了改變資料,所以不能同時進行讀取或則其他寫入操作。
X | S | |
---|---|---|
X | 不相容 | 不相容 |
S | 不相容 | 相容 |
InnoDB儲存引擎支援多粒度鎖定,這種鎖定允許事務在行級上的鎖和表級上的鎖同時存在。為了支援在不同粒度上進行加鎖操作,InnoDB儲存引擎支援一種稱為意向鎖的鎖方式。意向鎖是將鎖定的物件分為多個層次,意向鎖意味著事務希望在更細粒度上進行加鎖。
InnoDB儲存引擎的意向鎖即為表級別的鎖。設計目的主要是為了在一個事務中揭示下一行將被請求的鎖型別。其支援兩種意向鎖:
- 意向共享鎖(IS Lock),事務想要獲得一張表中某幾行的共享鎖
- 意向排他鎖(IX Lock),事務想要獲得一張表中某幾行的排他鎖
需要注意的是意向鎖是表級別的鎖,它不會和行級的X,S鎖發生衝突。只會和表級的X,S發生衝突。故表級別的意向鎖和表級別的鎖的相容性如下表所示。
IS | IX | S | X | |
---|---|---|---|---|
IS | 相容 | 相容 | 相容 | 不相容 |
IX | 相容 | 相容 | 不相容 | 不相容 |
S | 相容 | 不相容 | 相容 | 不相容 |
X | 不相容 | 不相容 | 不相容 | 不相容 |
向一個表新增表級X鎖的時候(執行ALTER TABLE, DROP TABLE, LOCK TABLES等操作),如果沒有意向鎖的話,則需要遍歷所有整個表判斷是否有行鎖的存在,以免發生衝突。如果有了意向鎖,只需要判斷該意向鎖與即將新增的表級鎖是否相容即可。因為意向鎖的存在代表了,有行級鎖的存在或者即將有行級鎖的存在,因而無需遍歷整個表,即可獲取結果。
如果將上鎖的物件看成一棵樹,那麼對最下層的物件上鎖,也就是對最細粒度的物件進行上鎖,那麼首先需要對粗粒度的物件上鎖。如上圖所示,如果需要對錶1的記錄m行上X鎖,那麼需要先對錶1加意向IX鎖,然後對記錄m上X鎖。如果其中任何一個部分導致等待,那麼該操作需要等待粗粒度鎖的完成。
InnoDB鎖相關狀態查詢
使用者可以使用INFOMATION_SCHEMA庫下的INNODB_TRX、INNODB_LOCKS和INNODB_LOCK_WAITS表來監控當前事務並分析可能出現的鎖問題。INNODB_TRX的定義如下表所示,其由8個欄位組成。
欄位名 | 說明 |
---|---|
trx_id | InnoDB儲存引擎內部唯一的事務ID |
trx_state | 當前事務的狀態 |
trx_started | 事務的開始時間 |
trx_request_lock_id | 等待事務的鎖ID。如果trx_state的狀態為LOCK WAIT,那麼該欄位代表當前事務等待之前事務佔用的鎖資源ID |
trx_wait_started | 事務等待的時間 |
trx_weight | 事務的權重,反映了一個事務修改和鎖住的行數,當發生死鎖需要回滾時,會選擇該數值最小的進行回滾 |
trx_mysql_thread_id | 執行緒ID,SHOW PROCESSLIST 顯示的結果 |
trx_query | 事務執行的SQL語句 |
mysql> SELECT * FROM information_schema.INNODB_TRX\G;
************************************* 1.row *********************************************
trx_id: 7311F4
trx_state: LOCK WAIT
trx_started: 2010-01-04 10:49:33
trx_requested_lock_id: 7311F4:96:3:2
trx_wait_started: 2010-01-04 10:49:33
trx_weight: 2
trx_mysql_thread_id: 471719
trx_query: select * from parent lock in share mode
複製程式碼
INNODB_TRX表只能顯示當前執行的InnoDB事務,並不能直接判斷鎖的一些情況。如果需要檢視鎖,則還需要訪問表INNODB_LOCKS,該表的欄位組成如下表所示。
欄位名 | 說明 |
---|---|
lock_id | 鎖的ID |
lock_trx_id | 事務的ID |
lock_mode | 鎖的模式 |
lock_type | 鎖的型別,表鎖還是行鎖 |
lock_table | 要加鎖的表 |
lock_index | 鎖住的索引 |
lock_space | 鎖住的space id |
lock_page | 事務鎖定頁的數量,若是表鎖,則該值為NULL |
lock_rec | 事務鎖定行的數量,如果是表鎖,則該值為NULL |
lock_data | 事務鎖住記錄的主鍵值,如果是表鎖,則該值為NULL |
mysql> SELECT * FROM information_schema.INNODB_LOCKS\G;
*************************************** 1.row *************************************
lock_id: 7311F4:96:3:2
lock_trx_id: 7311F4
lock_mode: S
lock_type: RECORD
lock_type: 'mytest'.'parent'
lock_index: 'PRIMARY'
lock_space: 96
lock_page: 3
lock_rec: 2
lock_data: 1
複製程式碼
通過表INNODB_LOCKS檢視每張表上鎖的情況後,使用者就可以來判斷由此引發的等待情況。當時當事務量非常大,其中鎖和等待也時常發生,這個時候就不那麼容易判斷。但是通過表INNODB_LOCK_WAITS,可以很直觀的反應當前事務的等待。表INNODB_LOCK_WAITS由四個欄位組成,如下表所示。
欄位名 | 說明 |
---|---|
requesting_trx_id | 申請鎖資源的事務ID |
requesting_lock_id | 申請的鎖的ID |
blocking_trx_id | 阻塞的事務ID |
blocking_lock_id | 阻塞的鎖的ID |
mysql> SELECT * FROM information_schema.INNODB_LOCK_WAITS\G;
*******************************************1.row************************************
requesting_trx_id: 7311F4
requesting_lock_id: 7311F4:96:3:2
blocking_trx_id: 730FEE
blocking_lock_id: 730FEE:96:3:2
複製程式碼
通過上述的SQL語句,使用者可以清楚直觀地看到哪個事務阻塞了另一個事務,然後使用上述的事務ID和鎖ID,去INNODB_TRX和INNDOB_LOCKS表中檢視更加詳細的資訊。
後記
我們後續還會學習InnoDB的一致性非鎖定讀相關的知識,請大家持續關注。