<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          [面試避險(xiǎn)] MySQL for update 到底是 row lock / table lock

          共 5158字,需瀏覽 11分鐘

           ·

          2022-07-08 21:32

          ????關(guān)注后回復(fù) “進(jìn)群” ,拉你進(jìn)程序員交流群????


          作者: _沸羊羊_
          來(lái)源: juejin.cn/post/7006522876207562759

          Part1前言

          MySQL 在使用 for update 進(jìn)行查詢(xún)操作時(shí),鎖的是 row 還是 table 呢?答案見(jiàn)文末。

          本文測(cè)試的環(huán)境為 MySQL 8.0.21

          Part2驗(yàn)證

          MySQL for update 時(shí)使用索引 檢索數(shù)據(jù)的情況下,使用的是 row lock,而不使用索引檢索數(shù)據(jù)的話,是 table lock,下面我們先來(lái)通過(guò)實(shí)驗(yàn)驗(yàn)證這個(gè)說(shuō)法。

          打開(kāi)兩個(gè)MySQL連接,將其中一個(gè)連接關(guān)閉自動(dòng)提交事務(wù)。

          -- 查詢(xún)事務(wù)提交方式
          select @@autocommit;
          -- 關(guān)閉自動(dòng)提交事務(wù)
          set autocommit = 0;

          現(xiàn)在有一個(gè) user 表,表中存儲(chǔ)數(shù)據(jù)如下:

          表中索引結(jié)構(gòu)是只有主鍵為聚集索引。

          1主鍵索引檢索數(shù)據(jù)

          連接1

          begin;

          select * from user where id = "1" for update;

          連接2

          update `user` set `name` = "feiyangyang" where id = "1";

          由于連接1未提交事務(wù),所以 id="1" 的行記錄被加了鎖,導(dǎo)致連接2寫(xiě)數(shù)據(jù)失敗。

          update `user` set `name` = "feiyangyang" where id = "1"
          > 1205 - Lock wait timeout exceeded; try restarting transaction
          > 時(shí)間: 50.403s

          再對(duì) id="2" 的數(shù)據(jù)進(jìn)行 update 操作

          update `user` set `name` = "feiyangyang" where id = "2";
          update `user` set `name` = "feiyangyang" where id = "2"
          > Affected rows: 1
          > 時(shí)間: 0.002s

          上述實(shí)驗(yàn)測(cè)試的有數(shù)據(jù)的情況,如果無(wú)數(shù)據(jù)的情況呢?

          連接1

          begin;

          select * from user where id = "4" for update;

          連接2

          update `user` set `name` = "feiyangyang" where id = "4";

          發(fā)現(xiàn)沒(méi)有數(shù)據(jù)的情況下沒(méi)有鎖

          update `user` set `name` = "feiyangyang" where id = "4"
          > Affected rows: 0
          > 時(shí)間: 0s

          現(xiàn)在確定了 id="1"的記錄是被加了行鎖(row lock),得出結(jié)論:

          結(jié)論:根據(jù)主鍵索引檢索數(shù)據(jù)時(shí),row lock(有數(shù)據(jù)),no lock(無(wú)數(shù)據(jù))

          2根據(jù)主鍵索引和非索引字段檢索數(shù)據(jù)

          連接1

          begin;

          select * from `user` where id = "1" and `name` = "test" for update;

          連接2

          UPDATE `user` 
          SET `name` = "feiyangyang" 
          WHERE id = "1";

          由于連接1未提交事務(wù),數(shù)據(jù)行被鎖,導(dǎo)致連接2 update 失敗

          > 1205 - Lock wait timeout exceeded; try restarting transaction
          > 時(shí)間: 51.451s

          此時(shí),不提交連接1的事務(wù),使用連接2查詢(xún)其他行記錄,執(zhí)行成功。

          UPDATE `user` 
          SET `name` = "feiyangyang" 
          WHERE id = "2";

          再來(lái)看看無(wú)數(shù)據(jù)的情況

          UPDATE `user` 
          SET `name` = "feiyangyang" 
          WHERE id = "4";
          > Affected rows: 0
          > 時(shí)間: 0s

          結(jié)論:根據(jù)主鍵索引和普通字段檢索數(shù)據(jù),row lock(有數(shù)據(jù)), no lock(無(wú)數(shù)據(jù))

          3根據(jù)非索引字段檢索數(shù)據(jù)

          連接1

          begin;

          select * from `user` where `name` = "test" for update;

          連接2

          UPDATE `user` 
          SET pwd = "feiyangyang" 
          WHERE `name`"test";

          > 1205 - Lock wait timeout exceeded; try restarting transaction
          > 時(shí)間: 50.385s

          UPDATE `user` 
          SET pwd = "feiyangyang" 
          WHERE `name`"feiyangyang";

          > 1205 - Lock wait timeout exceeded; try restarting transaction
          > 時(shí)間: 50.385s

          連接2 中執(zhí)行的第一個(gè) sql 語(yǔ)句與連接1中操作中的是同一個(gè)記錄行,由于連接1未提交事務(wù),所以連接2 更新失敗; 但第二個(gè) sql 語(yǔ)句與連接1并不是同一個(gè)記錄行,依然更新失敗。

          再來(lái)看看無(wú)數(shù)據(jù)的情況:

          連接2

          UPDATE `user` SET pwd = "feiyangyang" WHERE `name`"xiyangyang"
          > Affected rows: 0
          > 時(shí)間: 0.001s

          結(jié)論:根據(jù)非索引字段檢索數(shù)據(jù),table lock(有數(shù)據(jù)) no lock(無(wú)數(shù)據(jù))

          4根據(jù)普通索引檢索數(shù)據(jù)

          為 1.3 中的 name 字段添加索引

          create index idx_name on `user`(`name`);

          連接1

          begin;

          select * from `user` where `name` = "test" for update;

          連接2

          UPDATE `user` 
          SET pwd = "feiyangyang" 
          WHERE `name`"test";

          > 1205 - Lock wait timeout exceeded; try restarting transaction
          > 時(shí)間: 50.385s

          UPDATE `user` 
          SET pwd = "feiyangyang" 
          WHERE `name`"feiyangyang";

          > Affected rows: 1
          > 時(shí)間: 0.013s

          無(wú)數(shù)據(jù)的情況同上,略。

          結(jié)論:根據(jù)普通索引檢索,row lock(有數(shù)據(jù)),no lock(無(wú)數(shù)據(jù))

          5根據(jù) unique 索引檢索數(shù)據(jù)

          將 name 字段的索引修改為 唯一索引

          drop index idx_name on `user`;
          create unique index idx_name on `user`(name);

          連接1

          begin;

          select * from `user` where `name` = "test" for update;

          連接2

          UPDATE `user` 
          SET pwd = "feiyangyang" 
          WHERE `name`"test";

          > 1205 - Lock wait timeout exceeded; try restarting transaction
          > 時(shí)間: 50.374s

          UPDATE `user` 
          SET pwd = "feiyangyang" 
          WHERE `name`"feiyangyang";

          > Affected rows: 1
          > 時(shí)間: 0.05s

          無(wú)數(shù)據(jù)的情況同上,略。

          結(jié)論:根據(jù)唯一索引檢索數(shù)據(jù)時(shí),row lock (有數(shù)據(jù)),no lock(無(wú)數(shù)據(jù))

          Part3總結(jié)

          當(dāng)對(duì)索引列檢索數(shù)據(jù)時(shí),有數(shù)據(jù)的情況下,鎖的級(jí)別是記錄行;而根據(jù)非索引字段檢索數(shù)據(jù)時(shí),有數(shù)據(jù)的情況下,鎖的級(jí)別是整個(gè)表。

          MySQL 進(jìn)行 row lock 還是 table lock 只取決于是否使用了索引,當(dāng)進(jìn)行一些讓索引失效的操作時(shí),自然進(jìn)行的是 table lock 了。

          那問(wèn)題來(lái)了,為什么對(duì)索引字段加排他鎖,鎖的是行記錄,對(duì)非索引字段加鎖,鎖的是整個(gè)表?

          排他鎖鎖的是索引項(xiàng),個(gè)人理解就是B+樹(shù)的葉子節(jié)點(diǎn),當(dāng)對(duì)同一棵B+樹(shù)的其他葉子節(jié)點(diǎn)進(jìn)行寫(xiě)操作時(shí),是互不影響的。而如果是非索引字段,是沒(méi)有索引樹(shù)的結(jié)構(gòu)的,只能鎖整個(gè)表。

          -End-

          最近有一些小伙伴,讓我?guī)兔φ乙恍?nbsp;面試題 資料,于是我翻遍了收藏的 5T 資料后,匯總整理出來(lái),可以說(shuō)是程序員面試必備!所有資料都整理到網(wǎng)盤(pán)了,歡迎下載!

          點(diǎn)擊??卡片,關(guān)注后回復(fù)【面試題】即可獲取

          在看點(diǎn)這里好文分享給更多人↓↓

          瀏覽 34
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  狼人入口国产 | 蜜桃av秘 无码一区二区三 | 青娱网电信一区电信二区电信三区 | 亚洲成人中文网 | 五月色丁香国产精品 |