MySQL innoDB引擎鎖機制(一) —— 行鎖和表鎖

我們都知道,MyISAM引擎使用的是表鎖,而innoDB最小粒度爲行鎖。但在實際使用中我們有時發現就算我們操作的是不同行的數據,還是會發生鎖表。我們先來看一個例子。

session1開啓事務並更新id=1的數據:

wKiom1cXFPLQ06bWAAAgwAz71Jw721.png

session2開啓事務,並更新id=2的數據,但session2被阻塞了:

wKiom1cXFZHgjW-TAAALsd29rWo848.png

不是說innoDB支持行鎖嗎,我們這裏明明更新的不是同一條數據,爲什麼還會被阻塞。其實這是因爲MySQL innoDB給數據加鎖的方式和oracle不一樣。oracle是給這條數據行加鎖,而innoDB是給索引上的索引項加鎖來實現的。簡單的說就是:如果我們的語句無法命中索引,innoDB就會鎖表。我們給innodb_lock表的id列加上索引:

wKiom1cXGNXDjn6gAAAK2QMVP2U594.png

session1開啓事務並更新id=1的數據:

wKiom1cXGRTRHLdXAAARm0Aju2Y514.png

session2開啓事務,並更新id=2的數據,此時更新成功:

wKioL1cXGkWCJt0AAAATUASIqGI518.png




上面說到innoDB的行鎖是針對索引加鎖,而不是具體的記錄。所以如果即使是不同的記錄,只要訪問的是同一個索引也會導致阻塞。還是用上面的例子,我們先插入一條id相同的數據。

session1開啓事務並更新id=1 and name=my1的數據:

wKiom1cXHdXSrbVFAAAwcLnYi-c972.png

session2開啓事務,並更新id=1 and name=my5的數據,但session2被阻塞了:

wKioL1cXHueCTyqBAAANmh1EdQo713.png

我們再嘗試去更新name=my5的數據,但不用id去匹配,session2還是被阻塞。這種阻塞是因爲name列上無索引,所以該update會嘗試鎖表,但由於已經存在行鎖,所以鎖表會被阻塞:

wKioL1cXLiuTKf_GAAAHmQgYi_8712.png

從上面看出,innoDB鎖的就是索引,而不是表中的具體數據。由於session1鎖了id=1的索引,又由於name並無索引,所以他們雖然不是同一條記錄,但他們id是一樣的就會導致阻塞。




上面說的都是單列索引的情況,如果表上有多列都有索引會怎麼樣呢。當表有多個索引時,可以使用不同的索引鎖定不同行。我們修改下表結構,加入一列addr,然後給id和name都加上索引。

wKioL1cXQb_BQV3jAAAU5IWIuKQ227.png

我們先來看看使用一列索引的情況。session1更新id=1 and addr=addr1的行:

wKiom1cXQZbwLWzsAAAT7sN8kJI521.png

session2去更新name=my4的行,被阻塞。因爲雖然MySQL嘗試去鎖行但該行id=1的索引已經被鎖定:

wKiom1cXQo3BK64iAAAGByJTHg4852.png

session2再用addr=addr4去更新,被阻塞。這裏被阻塞是該語句嘗試去鎖表,因爲addr列上無索引:

wKiom1cXQ2aCpMoXAAAFcHBcYXk792.png


我們再來看看使用多列索引的情況。session1更新id=1 and name=my1的行:

wKioL1cXRYnCRFxUAAATuDyg-Lk576.png

session2去更新name=my4的行,成功。因爲id和name都有索引,所以當我們以它們爲條件時相當於形成了一個聯合索引,只精確的鎖了id=1 and name=my1的聯合索引。

wKioL1cXRlqCfUAvAAAfuHe3hpg377.png

session2再用addr=addr4去更新,還是被阻塞。這裏被阻塞是該語句嘗試去鎖表,因爲addr列上無索引:

wKiom1cXRgejVuCSAAAIc5_DrmI969.png




總結:MySQL innoDB給數據加鎖的方式和oracle不一樣。oracle是給這條數據行加鎖,而innoDB是給索引上的索引項加鎖來實現的。簡單的說就是:如果我們的語句無法命中索引,innoDB就會鎖表。我們可以通過幾個簡單地步驟來判斷innoDB會鎖哪些表:1.去掉where條件中沒有索引的列 2.用剩下的條件查詢出來的記錄就是innoDB會鎖的行。



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