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