前言
作者在工作中遇到了往MySQL表中插入一條數據,結果插入不進去。並且同樣的操作在Oracle中卻可以插入,於是就專門研究了一下MySQL5.7鎖的邏輯。
鎖的介紹
介紹鎖之前需要先了解一下事務隔離級別:https://www.cnblogs.com/jssj/p/13437036.html
MySQL 主要鎖類型如下
(1)共享/排它鎖(Shared and Exclusive Locks)
共享鎖:select ...... lock in share mode; 其他事務無法修改,發送在讀取操作。
排它鎖:select ...... for update; 其他事務無法修改,發生新增修改操作
(2)意向鎖(Intention Locks)
就是從行鎖升級到表鎖。
(3)記錄鎖(Record Locks)
對記錄行進行更新操作時候的一種鎖,防止事務1更新的時候,事務2也更新,導致最終數據問題。
(4)間隙鎖(Gap Locks)
範圍更新數據的時候,將這些範圍裏面數據中間的間隙也不能被修改或者插入。比如更新主鍵id 10 到 100 之前的數據,這個時候其他事務是無法插入 id等 10到100之間的記錄的。
(5)臨鍵鎖(Next-key Locks)
將行鎖和間隙鎖結合:將間隙和大於目前索引值的全部鎖定,不讓其他事務插入。
(6)插入意向鎖(Insert Intention Locks)
插入意向鎖是一種特殊的間隙鎖,但不同於間隙鎖的是,該鎖只用於併發插入操作。如果說間隙鎖鎖住的是一個區間,那麼插入意向鎖鎖住的就是一個點。
(7)自增鎖(Auto-inc Locks)
AUTO-INC LOCKS是一種表級別鎖,當一個表格中存在AUTO_INCREMENT列,事務嘗試在該表中插入記錄時,會使用該中特殊的表級別鎖。最簡單的場景是,當A事務試圖在該表中插入記錄時,其他試圖在該表中插入記錄的事務必須等待A事務,以使A事務插入的記錄獲得連續的主鍵。
innodb_autoinc_lock_mode這個設置控制了Mysql使用何種算法進行auto-increment lock
我們看一個案例:
創建一張表:
CREATE TABLE test(id int primary key not null auto_increment, name VARCHAR(128));
插入一些數據
insert into test(name) VALUES('lili'); insert into test(name) VALUES('zhangsan'); insert into test(name) VALUES('leilei'); insert into test(name) VALUES('lisi'); insert into test(name) VALUES('wangwu'); insert into test(name) VALUES('zhaoliu');
測試:在一個地方開啓事務,然後刪除表數據並且不提交。
另外開啓一個窗口,測試新增語句:
等待超時無法提交,在等待結束之前我們來看看mysql 給我們提供的鎖表信息
select * from information_schema.innodb_trx t; -- 當前運行的所有事務 select * from information_schema.innodb_locks t; -- 當前出現的鎖 select * from information_schema.innodb_lock_waits t; -- 鎖等待的對應關係
innodb_trx 顯示運行事務的情況,很明顯有兩個事務在運行,其中被鎖事務的sql可以展示。
innodb_locks 該表比較重要的信息爲,可以爲我們提供兩個事務爲什麼會互斥導致出現鎖, 比如字段 lock_mode 是指具體的鎖類型,X表示互斥鎖,GAP表示間隙鎖
innodb_lock_waits 該表給我展示的是互斥資源之間的關係。
這三張表各個字段含義:
innodb_trx
innodb_locks
innodb_lock_waits
案例
我們在進行修改表的ddl 操作時:發現會一直執行下面,不會停止。
通過:show processlist;
會出現:Waiting for table metadata lock,在等待鎖的釋放。
可以通過查詢:
select * from information_schema.innodb_trx;
來找到running 的記錄並且 kill trx_mysql_thread_id ; 生產需要謹慎使用,因爲這邊是不知道別kill掉的是什麼?
下面在測試一個案例(該演示不要使用第三方工具例如Navicat 等):
一個窗口刪除數據,不提交。
另外一個窗口,更新這條記錄,會出現鎖等待。
這個時候我們查詢mysql的`performance_schema`.events_statements_current 表可以看到sql語句的執行情況。
如果生產數據量大的話其實也是不好排查的。
將mysql 表關聯起來找到引起鎖等等待的事務語句
SELECT r.trx_id waiting_trx_id, r.trx_mysql_thread_id waiting_thread, r.trx_query waiting_query, b.trx_id blocking_trx_id, b.trx_mysql_thread_id blocking_thread, b.trx_query blocking_query FROM information_schema.innodb_lock_waits w INNER JOIN information_schema.innodb_trx b ON b.trx_id = w.blocking_trx_id INNER JOIN information_schema.innodb_trx r ON r.trx_id = w.requesting_trx_id;
將圖中449作爲下面sql的條件:
SELECT a.sql_text, c.id, d.trx_started FROM `performance_schema`.events_statements_current a JOIN `performance_schema`.threads b ON a.thread_id = b.thread_id JOIN information_schema.`PROCESSLIST` c ON b.processlist_id = c.id JOIN information_schema.innodb_trx d ON c.id = d.trx_mysql_thread_id WHERE 1 = 1 and c.id = 449 -- blocking_thread 編號 ORDER BY d.trx_started;
這樣我們就找到了是什麼sql語句沒有提交事務,導致鎖等待的出現。
總結
MySQL查詢未提交事務的目前知曉的是一定要出現鎖等待才能查到,不然就獲取不到未提交事務的執行情況,如果有人知道請一定要分享給我,謝謝。