圖4 聚簇索引和二級索引
下面分析下索引和鎖的關係。
1)delete from msg where id=2;
由於id是主鍵,因此直接鎖住整行記錄即可。
圖52)delete from msg where token=』 cvs』;
由於token是二級索引,因此首先鎖住二級索引(兩行),接著會鎖住相應主鍵所對應的記錄;
3)delete from msg where message=訂單號是多少』;
message沒有索引,所以走的是全表掃瞄過濾。這時表上的各個記錄都將新增上x鎖。
圖71.2.2 鎖與隔離級別的關係
大學資料庫原理都學過,為了保證併發運算元據的正確性,資料庫都會有事務隔離級別的概念:1)未提交讀(read uncommitted);2)已提交讀(read committed(rc));3)可重複讀(repeatable read(rr));4)可序列化(serializable)。我們較常使用的是rc和rr。
提交讀(rc):只能讀取到已經提交的資料。
可重複讀(rr):在同乙個事務內的查詢都是事務開始時刻一致的,innodb預設級別。
我們在1.2.1節談論的其實是rc隔離級別下的鎖,它可以防止不同事務版本的資料修改提交時造成資料衝突的情況,但當別的事務插入資料時可能會出現問題。
如下圖所示,事務a在第一次查詢時得到1條記錄,在第二次執行相同查詢時卻得到兩條記錄。從事務a角度上看是見鬼了!這就是幻讀,rc級別下儘管加了行鎖,但還是避免不了幻讀。
圖8innodb的rr隔離級別可以避免幻讀發生,怎麼實現?當然需要借助於鎖了!
為了解決幻讀問題,innodb引入了gap鎖。
在事務a執行:update msg set message=『訂單』 where token=『asd』;
innodb首先會和rc級別一樣,給索引上的記錄新增上x鎖,此外,還在非唯一索引』asd』與相鄰兩個索引的區間加上鎖。
這樣,當事務b在執行insert into msg values (null,『asd』,』hello』); commit;時,會首先檢查這個區間是否被鎖上,如果被鎖上,則不能立即執行,需要等待該gap鎖被釋放。這樣就能避免幻讀問題。
圖93 死鎖成因
了解了innodb鎖的基本原理後,下面分析下死鎖的成因。如前面所說,死鎖一般是事務相互等待對方資源,最後形成環路造成的。下面簡單講下造成相互等待最後形成環路的例子。
3.1不同表相同記錄行鎖衝突
這種情況很好理解,事務a和事務b操作兩張表,但出現迴圈等待鎖情況。
圖103.2相同表記錄行鎖衝突
這種情況比較常見,之前遇到兩個job在執行資料批量更新時,joba處理的的id列表為[1,2,3,4],而job處理的id列表為[8,9,10,4,2],這樣就造成了死鎖。
圖113.3不同索引鎖衝突
這種情況比較隱晦,事務a在執行時,除了在二級索引加鎖外,還會在聚簇索引上加鎖,在聚簇索引上加鎖的順序是[1,4,2,3,5],而事務b執行時,只在聚簇索引上加鎖,加鎖順序是[1,2,3,4,5],這樣就造成了死鎖的可能性。
圖123.4 gap鎖衝突
innodb在rr級別下,如下的情況也會產生死鎖,比較隱晦。不清楚的同學可以自行根據上節的gap鎖原理分析下。
圖134 如何盡可能避免死鎖
1)以固定的順序訪問表和行。比如對第2節兩個job批量更新的情形,簡單方法是對id列表先排序,後執行,這樣就避免了交叉等待鎖的情形;又比如對於3.1節的情形,將兩個事務的sql順序調整為一致,也能避免死鎖。
2)大事務拆小。大事務更傾向於死鎖,如果業務允許,將大事務拆小。
3)在同乙個事務中,盡可能做到一次鎖定所需要的所有資源,減少死鎖概率。
4)降低隔離級別。如果業務允許,將隔離級別調低也是較好的選擇,比如將隔離級別從rr調整為rc,可以避免掉很多因為gap鎖造成的死鎖。
5)為表新增合理的索引。可以看到如果不走索引將會為表的每一行記錄新增上鎖,死鎖的概率大大增大。
5 如何定位死鎖成因
下面以本文開頭的死鎖案例為例,講下如何排查死鎖成因。
1)通過應用業務日誌定位到問題**,找到相應的事務對應的sql;
因為死鎖被檢測到後會回滾,這些資訊都會以異常反應在應用的業務日誌中,通過這些日誌我們可以定位到相應的**,並把事務的sql給梳理出來。
index merge導致MySql死鎖問題
index merge 導致線上死鎖問題 問題造成條件 兩個事務分別執行兩次update操作 每個用到了兩個及以上索引 由於兩邊索引順序不一致導致 造成原因 innodb使用索引來實現行級別的鎖,事務a 通過index1 對某個欄位加了鎖 事務b 通過index2 對另乙個欄位加了鎖 兩個事務都持有...
Mysq遇到的問題整理更新
1 lock wait timeout exceeded try restarting transaction的問題解決 解決辦法 a 檢視當前的事務隔離級別 b 檢視當前資料庫的執行緒情況 c 沒有看到正在執行的很慢sql記錄執行緒,再去檢視innodb的事務表innodb trx,看下裡面是否有...
mysql 死鎖模擬 Mysql的死鎖
專案經常會出現mysql的死鎖問題,當年年少總是想通過select from information schema.innodb locks 檢視被鎖的事務,然後kill掉他,或者重啟mysql,唉,治標不治本啊,下次還會出現這些問題,其實造成死鎖大多數情況就是我們的sql寫的不大好。我們先來模擬一...