【转】【MySQL】事务与锁(四):行锁到底锁住的是什么?记录?字段?索引?
首先我們有三張表t1,t2,t3,它們都是只有兩個(gè)字段, int類型的id和varchar類型的name;區(qū)別是t1沒有索引,t2有主鍵索引,t3有唯一索引。
再?gòu)?qiáng)調(diào)一次,在實(shí)驗(yàn)前必須提前關(guān)閉自動(dòng)提交,set autocommit=off。然后show variables like 'autocommit'查看自動(dòng)提交是否是off。
我們先假設(shè)InnoDB的鎖鎖住了是一行數(shù)據(jù)或者一條記錄。
1.假設(shè)鎖住記錄
1.1 實(shí)驗(yàn)一:沒有索引的表(t1)
這個(gè)實(shí)驗(yàn)操作是操作沒有索引的t1,t1里面有4條數(shù)據(jù):1、2、3、4。
現(xiàn)在我們?cè)趦蓚€(gè)會(huì)話里面手工開啟兩個(gè)事務(wù)。在第一個(gè)事務(wù)里面,我們通過(guò)where id =1 鎖住第一行數(shù)據(jù)。在第二個(gè)事務(wù)里面,我們嘗試給id=3的這一行數(shù)據(jù)加鎖,大家覺得能成功嗎?
| Begin; | ? |
| SELECT * FROM t1 where id=1 FOR UPDATE; | ? |
| ? | Begin; |
| ? | SELECT * FROM t1 where id=3 FOR UPDATE; // BLOCKED |
| ? | INSERT INTO t1 (id, name) VALUES (5, ‘5’); // BLOCKED |
這就有點(diǎn)奇怪了,第一個(gè)事務(wù)鎖住了id=1的這行數(shù)據(jù),為什么我不能操作id=3的數(shù)據(jù)呢?我們?cè)賮?lái)操作一條不存在的數(shù)據(jù),插入id=5。它也被阻塞了。實(shí)際上這里整張表都被鎖住了。所以,我們的第一個(gè)猜想被推翻了,InnoDB的鎖鎖住的應(yīng)該不是Record。
那為什么在沒有索引或者沒有用到索引的情況下,會(huì)鎖住整張表?這個(gè)問(wèn)題我們先留在這里。下面繼續(xù)看第二個(gè)實(shí)驗(yàn)。
1.2 實(shí)驗(yàn)二:有主鍵索引的表(t2)
我們先看一下t2的表結(jié)構(gòu)。字段是一樣的,不同的地方是id上創(chuàng)建了一個(gè)主鍵索引。里面的數(shù)據(jù)是 1、4、7、10。
| Begin; | ? |
| SELECT * FROM t2 where id=1 FOR UPDATE; | ? |
| ? | Begin; |
| ? | SELECT * FROM t2 where id=1 FOR UPDATE; // BLOCKED |
| ? | SELECT * FROM t2 where id=4 FOR UPDATE; // OK |
第一種情況,使用相同的id值去加鎖,沖突;使用不同的id加鎖,可以加鎖成功。
那么出現(xiàn)問(wèn)題了,從實(shí)驗(yàn)一中得到鎖定的不是一行數(shù)據(jù),但是實(shí)驗(yàn)二操作不同記錄的數(shù)據(jù)又可以成功,那有沒有可能是鎖住了id的這個(gè)字段呢?
2.假設(shè)鎖住字段
我們看一下 t3 的表結(jié)構(gòu)。字段還是一樣的, id上創(chuàng)建了一個(gè)主鍵索引,name上創(chuàng)建了一個(gè)唯一索引。里面的數(shù)據(jù)是1、4、7、10。
在第一個(gè)事務(wù)里面,我們通過(guò)name字段去鎖定值是4的這行數(shù)據(jù)。在第二個(gè)事務(wù)里面,嘗試獲取一樣的排它鎖,肯定是失敗的,這個(gè)不用懷疑。在這里我們懷疑InnoDB鎖住的是字段,所以這次我換一個(gè)字段,用id=4去給這行數(shù)據(jù)加鎖,大家覺得能成功嗎?
| Begin; | ? |
| SELECT * FROM t3 where name=‘4’ FOR UPDATE; | ? |
| ? | Begin; |
| ? | SELECT * FROM t3 where name=‘4’ FOR UPDATE; // BLOCKED |
| ? | SELECT * FROM t3 where id=4 FOR UPDATE; // BLOCKED |
很遺憾,又被阻塞了,說(shuō)明鎖住的是字段的這個(gè)推測(cè)也是錯(cuò)的,否則就不會(huì)出現(xiàn)第一個(gè)事務(wù)鎖住了name,第二個(gè)字段鎖住id失敗的情況。
既然鎖住的不是record,也不是column, InnoDB里面鎖住的到底是什么呢?
3.其實(shí),鎖的是索引
在這三個(gè)案例里面,我們要去分析一下他們的差異在哪里,也就是這三張表的結(jié)構(gòu),是什么區(qū)別導(dǎo)致了加鎖的行為的差異?其實(shí)答案就是索引。?InnoDB的行鎖,就是通過(guò)鎖住索引來(lái)實(shí)現(xiàn)的。
那索引又是個(gè)什么東西?為什么它可以被鎖住?我們?cè)?【MySQL】詳談索引存儲(chǔ)結(jié)構(gòu)推演過(guò)程?已經(jīng)分析過(guò)了。那么我們還有兩個(gè)問(wèn)題沒有解決:
問(wèn)題一:為什么表里面沒有索引的時(shí)候,實(shí)驗(yàn)一鎖住一行數(shù)據(jù)會(huì)導(dǎo)致鎖表?或者說(shuō),如果鎖住的是索引,一張表沒有索引怎么辦?所以,一張表有沒有可能沒有索引?
所以,實(shí)驗(yàn)一為什么鎖表,是因?yàn)椴樵儧]有使用索引,會(huì)進(jìn)行全表掃描,然后把每一個(gè)隱藏的聚集索引都鎖住了。
問(wèn)題二:實(shí)驗(yàn)二為什么通過(guò)唯一索引給數(shù)據(jù)行加鎖,主鍵索引也會(huì)被鎖住?
在輔助索引里面, 索引存儲(chǔ)的是二級(jí)索引和主鍵的值。 比如name=4,存儲(chǔ)的是name的索引和主鍵id的值4。而主鍵索引里面除了索引之外,還存儲(chǔ)了完整的數(shù)據(jù)。所以我們通過(guò)輔助索引鎖定一行數(shù)據(jù)的時(shí)候,它跟我們檢索數(shù)據(jù)的步驟是一樣的,會(huì)通過(guò)主鍵值找到主鍵索引,然后也鎖定。
?
復(fù)雜官網(wǎng)的一句話,A record lock is a lock on an index record,是不是只有【記錄鎖】是鎖索引,其他鎖是鎖住行或表?
回復(fù):行鎖、表鎖是對(duì)于鎖粒度而言的,是一對(duì)最廣泛的概念。表鎖的實(shí)現(xiàn)很好想,就是需要一個(gè)標(biāo)志來(lái)記錄當(dāng)前有沒有事務(wù)已經(jīng)來(lái)操做表了。而行鎖的實(shí)現(xiàn)是鎖的索引,根據(jù)鎖索引的范圍又可以分為記錄鎖、間隙鎖、臨鍵鎖。 比如說(shuō)我們修改一個(gè)數(shù)據(jù)庫(kù)已經(jīng)有的記錄,那直接鎖相應(yīng)索引就行(記錄鎖);再比如我們給一張有兩條數(shù)據(jù)的表(id=1,id=10)進(jìn)行范圍查詢并加鎖 where id>2 and id < 5 for update,此時(shí)命中了一個(gè)不存在數(shù)據(jù)的區(qū)間(2,5),這時(shí)該鎖哪?是鎖兩個(gè)索引之間的整個(gè)區(qū)間(1,10),這就是間隙鎖。臨鍵鎖=記錄鎖+間隙鎖,這里就不說(shuō)了,可以看我的這篇文章 https://blog.csdn.net/weixin_43935927/article/details/109410420
總結(jié)
以上是生活随笔為你收集整理的【转】【MySQL】事务与锁(四):行锁到底锁住的是什么?记录?字段?索引?的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 春节信用卡提额度最快方法
- 下一篇: 【转】Magento2 数据库操作