《MySQL數據庫》MySQL鎖分析

前言

作者在工作中遇到了往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查詢未提交事務的目前知曉的是一定要出現鎖等待才能查到,不然就獲取不到未提交事務的執行情況,如果有人知道請一定要分享給我,謝謝。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章