<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      工作面試老大難-MySQL中的鎖類型

      MySQL 是支持ACID特性的數據庫。我們都知道”C”代表Consistent,當不同事務操作同一行記錄時,為了保證一致性,需要對記錄加鎖。在MySQL 中,不同的引擎下的鎖行為也會不同,本文將重點介紹 MySQL InnoDB引擎中常見的鎖。

      一. 準備

      CREATE TABLE `user` (
        `id` bigint(20) NOT NULL AUTO_INCREMENT,
        `name` varchar(32) DEFAULT NULL,
        `age` tinyint(4) DEFAULT '0',
        `phone` varchar(11) DEFAULT NULL,
        PRIMARY KEY (`id`),
        KEY `idx_age` (`age`)
      ) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4;
      
      create index test_age_index
          on user (age);
      
      #插入基礎數據
      INSERT INTO `user` (`id`, `name`, `age`, `phone`)
      VALUES
      	(1, '張三', 18, '13800138000'),
      	(2, '李四', 20, '13800138001'),
      	(3, '王五', 22, '13800138002'),
      	(4, '趙六', 26, '13800138003'),
      	(5, '孫七', 30, '13800138004');
      

      為了方便講解,創建一張user表,設置age的字段為普通索引,并填充以下數據。本文所有的sql語句均基于這張表。

      idnameagephone
      1張三1813800138000
      2李四2013800138001
      3王五2213800138002
      4趙六2613800138003
      5孫七3013800138004

      二. 快照讀和當前讀

      MySQL在REPEATABLE READ隔離級別下很大程度地避免了幻讀現象(很大程度是個啥意思?意思是在某些情況下其實還是可能出現幻讀現象的)。

      怎么避免臟讀、不可重復讀、幻讀這些現象呢?其實有兩種可選的解決方案。

      • 方案一:讀操作使用多版本并發控制(MVCC),寫操作進行加鎖。

        MVCC 在之前的文章有詳細的描述,就是通過生成一個 ReadView,然后通過ReadView找到符合條件的記錄版本(歷史版本是由undo日志構建的)。其實就像是在生成ReadView的那個時刻,時間靜止了(就像用相機拍了一個快照),查詢語句只能讀到在生成ReadView之前已提交事務所做的更改,在生成ReadView之前未提交的事務或者之后才開啟的事務所做的更改則是看不到的。寫操作肯定針對的是最新版本的記錄,讀記錄的歷史版本和改動記錄的最新版本這兩者并不沖突,也就是采用MVCC時,讀-寫操作并不沖突。我們通常把MVCC實現的并發讀寫稱為“快照讀”

        MVCC無法完全避幻讀,參考:《什么是MVCC機制》

      • 方案二:讀、寫操作都采用加鎖的方式。

        如果我們的一些業務場不允許讀取記錄的舊版本,而是每次都必須去讀取記錄的最新版本。比如在銀行存款的事務中,我們需要先把賬戶的余額讀出來,然后將其加上本次存款的數額,最后再寫到數據庫中。在將賬戶余額讀取出來后,就不想讓別的事務再訪問該余額,直到本次存款事務執行完成后,其他事務才可以訪問賬戶的余額。這樣在讀取記錄的時候也就需要對其進行加鎖操作,這也就意味著讀操作和寫操作也得像寫寫操作 那樣排隊執行。我們通常將使用加鎖的方式實現的并發讀寫稱為“當前讀”。后文提到的select ... for updateselect ... lock in share mode 就是典型的當前讀。

      很明顯如果采用MVCC方式,讀-寫操作彼此并不沖突,性能更高;如果采用加鎖方式,讀-寫操作彼此需要排隊執行,從而影響性能。一般情況下,我們當然愿意采用MVCC來解決讀-寫操作并發執行的問題,但是在某些特殊業務場景中,要求必須采用加鎖的方式執行,那也是沒有辦法的事情。

      三. 鎖的分類

      3.1 行級鎖和表級鎖(Row-level and Table-level Locks)

      按照鎖的粒度劃分,可分為行級鎖和表級鎖。表級鎖作用于數據庫表,不同的事務對同一個表加鎖,根據實際情況,后加鎖的事務可能會發生block,直到表鎖被釋放。表級鎖的優點是資源占用低,可防止死鎖等。缺點是鎖的粒度太高,不利于高并發的場景。

      行級鎖行級鎖作用于數據庫行,它允許多個事務同時訪問同一個數據庫表。當多個事務操作同一行記錄時,沒獲得鎖的事務必須等持有鎖的事務釋放才能操作行數據。行級鎖的優點能支持較高的并發。缺點是資源占用較高,且會出現死鎖。

      4.2 共享鎖排它鎖(Shared and Exclusive Locks)

      InnoDB引擎的鎖分為兩類,分別是共享鎖和排他鎖。這些概念在很多領域都出現過,比如Java中的ReadWriteLock

      • 共享鎖(shared lock) 允許多個事務同時持有同一個資源的共享鎖,常用S表示。

        #mysql 8.0之前的版本通過 lock in share mode給數據行添加share lock
        select * from user where id = 1 lock in share mode;
          
        #mysql 8.0以后的版本通過for share給數據行添加share lock
        select * from user where id = 1 for share

        在普通的 SELECT 語句后邊加 LOCK IN SHARE MODE,如果當前事務執行了該語句,那么它會為讀取到的記錄加 S 鎖 這樣允許別的事務繼續獲取這些記錄的 S 鎖(比方說別的事務也使用 SELECT ... LOCK IN SHARE MODE 語句來讀取這些記錄),但是不能獲取這些記錄的 X 鎖(比方說使用 SELECT ... FOR UPDATE語句來讀取這些記錄,或者直接修改這些記錄)。如果別的事務想要獲取這些記錄的 X 鎖,那么它們會阻塞,直到當前事務提交之后將這些記錄上的 S 鎖釋放掉。

        select默認情況下都是快照讀,除非顯式加鎖,實現當前讀。

      • 排他鎖(exclusive lock)只允許一個事務持有某個資源的鎖,常用X表示。

        # 通過for update可以給數據行加exclusive lock
        select * from user where id = 1 for update;
          
        # 通過update或delete同樣也可以
        update user set age = 16 where id = 1;
        

        也就是在普通的 SELECT 語句后面加上 FOR UPDATE。如果當前事務執行了該語句,那么它會為讀取到的記錄加X鎖,這樣既不允許別的事務獲取這些記錄的S鎖(比如別的事務| 使用SELECT .. LOCK IN SHARE MODE語句來讀取這些記錄時),也不允許獲取這些記錄的x鎖(比如說使用SELECT … FOR UPDATE語句來讀取這些記錄,或者直接改動這些記錄時)如果別的事務想要獲取這些記錄的s鎖或者X鎖,那么它們會被阻塞,直到當物事務提交之后將這些記錄上的X鎖釋放掉為止。

        update、delete語句默認會加排他行鎖

      舉個例子,假如事務T1持有了某一行?的共享鎖(S)。當事務T2也想獲得該行的鎖,分為如下兩種情況:

      • 如果T2申請的是行r的共享鎖(S),會被立即允許,此時T1和T2同時持有行r的共享鎖。
      • 如果T2申請的是排他鎖(X),那么必須等T1釋放才能成功獲取。

      反過來說,假如T1持有行r的排他鎖,那不管T2申請的是共享鎖還是排他鎖,都必須等待T1釋放才能成功。

      總的來說,MySQL中的鎖有很多種,不過我們需要重點關注的就上面兩點,即鎖的作用域和鎖的類型。如上所述,鎖可以作用于行,也能作用于表,但不管他們的作用域是什么,鎖的類型只有兩種,即“共享”和“排他”

      不管是行級還是表級鎖,都遵循下列互斥關系:

      排他鎖(X)共享鎖(S)
      排他鎖(X)互斥互斥
      共享鎖(S)互斥兼容

      四. 數據庫鎖信息查看

      如果我們需要查看MySQL目前的鎖持有狀態,我們可以使用下列語句查詢:

      # 獲取 InnoDB 事務鎖的情況,MySQL 8.0 之前
      select * from information_schema.INNODB_LOCKS
      # MySQL 8.0 之后使用:
      select * from performance_schema.data_locks;
      

      例如下列SQL,

      Transaction 1Transaction 2Transaction 3
      select * from user where id=2 update;
      update user set name=‘張三’ where id=2;
      select * from performance_schema.data_locks;

      執行結果如下:

      通過 performance_schema.data_locks 表的信息,我們可以輕松了解到系統目前的加鎖情況。

      五. 意向鎖(Intention Locks)

      5.1 意向鎖分類

      InnoDB 支持多粒度鎖,允許行鎖和表鎖并存。例如, LOCK TABLES ... WRITE 之類的語句在指定的表上獲取排它鎖(X 鎖)。為了使多粒度級別的鎖定變得可行,InnoDB 使用了意向鎖。

      意向鎖是一種特殊的表級鎖,指示事務稍后對表中的行需要哪種類型的鎖(共享或獨占)。意向鎖有兩種類型:

      • 意向共享鎖(intention share lock):簡稱 IS事務在給一個數據行加共享鎖(S)前必須先取得該表的IS鎖,用于標記當前表有行級共享鎖存在,代表有事務準備讀取數據。
      • 意向排他鎖(intention exclusive lock):簡稱 IX。事務在給一個數據行加排他鎖(X)前必須先取得該表的IX鎖,用于標記當前表有行級排他鎖的存在,代表有事務準備寫入數據。

      需要注意的是,意向鎖不會阻塞除全表請求(例如 LOCK TABLES ... WRITE )之外的任何內容。意向鎖的主要目的是表明有人正在鎖定一行,或者將要鎖定表中的一行。IS 和 IX兩者之間并不互斥:

      意向排他鎖(IX)意向共享鎖(IS)
      意向排他鎖(IX)兼容兼容
      意向共享鎖(IS)兼容兼容

      也就是說,當 IX 被 T1事務獲取,并不影響其他事務獲取 IXIS;同理當 IST1獲取時,其他事務也能獲取到 IXIS

      只有當一個事務需要獲得表級X或S鎖時:

      # 給user表加表級 S 鎖
      lock tables user read;
      # 給user表加表級 X 鎖
      lock tables user write;
      

      才會去判斷當前表是否有人占用 IXIS 鎖,具體有兩種情況:

      1. 嘗試獲取表級S鎖時,如果 IX 被占用,這表明當前表有行級X鎖存在,會有事務寫入新數據,則獲取表級S鎖事務被阻塞;如果 IX 未被占用,這表明現在沒有行級X鎖存在,沒有事務寫入新數據,則成功獲取表級S鎖,。
      2. 嘗試獲取表級X鎖時,如果 IX 或 IS 被占用,這表明當前表有事務準備寫入或讀取某行數據,則獲取表級X鎖事務被阻塞。

      表級鎖和意向鎖的互斥關系如下表:

      意向共享鎖(IS)意向排他鎖(IX)
      共享鎖(S)兼容互斥
      排他鎖(X)互斥互斥

      5.2 意向鎖存在的意義

      有人可能會有疑問,MySQL為什么需要設計意向鎖呢?

      那我們就需要來看看沒有意向鎖,MySQL該如何處理表級鎖和行級鎖共存。

      假如事務 A 獲取了某一行的排他鎖,并未提交:

      SELECT * FROM `user` WHERE id = 1 FOR UPDATE;
      

      事務 B 想要獲取 users 表的表鎖:

      LOCK TABLES `user` READ;
      

      因為共享鎖與排他鎖互斥,所以事務 B 在試圖對 user 表加共享鎖的時候,必須保證:

      • 當前沒有其他事務持有 user 表的表級排他鎖。
      • 當前沒有其他事務持有 user 表中任意一行的行級排他鎖。

      為了檢測是否滿足第二個條件,事務 B 必須在確保 user 表不存在任何排他鎖的前提下,去檢測表中的每一行是否存在排他鎖。很明顯這是一個效率很差的做法,但是有了意向鎖之后,情況就不一樣了:

      因為行級鎖加鎖前,都會先獲取意向鎖,所以如果當前意向鎖沒有被占用,就代表當前表沒有行鎖占用,就不需要掃描整張表是否存在行級鎖占用,大大提高了表級鎖加鎖效率。

      六. 行鎖算法

      InnoDB引擎是MySQL非常重要的一部分,MySQL團隊為它開發了很多種類型的鎖,下面將逐一介紹。

      6.1 記錄鎖(Record Locks)

      Record Locks是作用于記錄的索引,可以鎖定單條或多條記錄,比如下面SQL語句:

      # 鎖定id=1這條記錄,阻止任何其他事務插入、更新或刪除 user.id 值為 1 的行。
       SELECT * FROM user WHERE id = 1 FOR UPDATE;
      

      上面的sql給id為1的行加了X鎖。其他事務要對這行數據進行修改(update、insert、delete)都必須等待當前事務釋放X鎖(提交或回滾事務)。

      記錄鎖總是鎖定索引記錄,即使表沒有定義索引。對于這種情況, InnoDB 創建一個隱藏的聚簇索引并使用該索引進行記錄鎖定。

      6.2 間隙鎖(Gap Locks)

      間隙鎖(Gap Lock)是InnoDB為了解決在“可重復讀”隔離級別下**“當前讀”的幻讀問題**引入的鎖機制。

      6.2.1 什么是間隙鎖

      間隙就是是指索引兩兩之間的一個左開右開區間。

      在user表中,由于age字段加了普通索引,age字段存在以下的間隙:

      (-∞,18), (18,20), (20,22), (22,26), (26,30), (30,+∞]

      6.2.2 間隙鎖的行為

      Record lock是作用于索引,而Gap locks 是作用于索引之間的間隙。比如下面的sql語句就會給(22,26)之間的索引間隙加鎖。

      select * from user where age between 22 and 26 for update;
      

      上面的語句執行過后,其他事務就無法往[22,26]之間的間隙插入數據。這樣做的目的是為了防止出現幻讀。假如沒有 Gap locks,下面sql會發生不同的行為:

      Transaction 1Transaction 2
      select * from user where age between 22 and 26 for update;
      Insert into user (name,age) values (‘bigbyto’,23);
      select * from user where age between 22 and 26;

      上面的sql可能會出現兩種情況

      • 有Gap locks:T1的第二次查詢依然是查詢出兩個結果,即王五和趙六。 T2將會Block,直到T1事務結束。通過下面sql可以看到Gap lock阻止了T2插入內容。
      • 沒有Gap locks:由于沒有 Gap locks,就只會給 id=3 和 id=4 加鎖,而不會給沒有的數據加鎖,這樣T2將會插入成功,數據庫多了一條bigbyto,age為25的數據; T1第二次查詢將會出現3條數據(幻讀)。

      Gap locks的目的就是為了防止其他事務往索引的間隙插入數據,以此來避免出現幻讀。雖然gap鎖有共享gap鎖和排他gap鎖這樣的說法,但是他們起的作用都是相同的。而且如果對一條記錄加了gap鎖(無論是共享gap鎖還是排他gap鎖),并不會限制其它事務對這條記錄加 Record Lock 或繼續加 gap 鎖。再強調一遍,gap鎖的作用僅僅是為了防止插入幻影記錄而已

      在 MySQL 的REPEATABLE-READ隔離級別下,Gap locks默認啟用。禁用方式很簡單,把隔離級別設置為READ_COMMITTED即可。

      需要注意的是,如果age列上沒有索引,SQL會走聚簇索引的全表掃描進行過濾,由于過濾是在MySQL Server層面進行的。因此每條記錄(無論是否滿足條件)都會被加上X鎖。

      也就是說如果 user 表沒有 age 字段索引,T1 執行 select * from user where age between 22 and 26 for update; 后,T2是無法插入任何數據。

      6.2.3 鎖降級

      REPEATABLE-READ隔離級別下,當查詢一條記錄時,根據實際情況,mysql會對記錄加不同的鎖。比如下面的sql:

      select * from user where id = 3 for update;
      

      上面sql中,會給id=3的行加Record lock。

      當where字段滿足唯一索引,主鍵其中之一時,mysql會使用Record lock給記錄加鎖。因為數據庫約束數據唯一,不會出現幻讀。如果字段是普通索引,情況會發生變化

      select * from user where age = 22 for update;
      

      上面的sql會使用Gap lock,(20,22)之間的間隙會被鎖定,其他事務無法往這個區間插入數據。

      6.3 Next-Key Locks

      Next-Key Locks實際上就是Gap lock和Record Lock的組合。Gap lock中的索引間隙是一個左開右開的區間,在next-key lock中,變成左開右閉,比如:

      (-∞,18], (18,20], (20,22], (22,26], (26,30], (30,+∞)

      Next-Key Locks同時給索引和索引之間的間隙加鎖(即組合Record lock和Gap lock),例如:

      select * from user where age = 22 for update;
      

      對于這條sql,鎖定的范圍變成了(18,22], (22,26),即Next-Key lock會鎖定索引前后的區間以及索引本身。同時,因為用到了Gap lock,這種鎖自然而然也是只有在 REPEATABLE-READ 的隔離級別下才能用。

      6.4 Insert Intention Locks

      Insert Intention Lock是MySQL中一種鎖類型,用于在多個事務同時向同一個表中插入新行時,保護對于同一索引鍵的插入操作。Insert Intention Lock的作用是在表級別上創建一個鎖定,以指示其他事務正在嘗試向表中插入新行。

      當一個事務想要向表中插入新行時,它會先獲得一個Insert Intention Lock。然后,如果該事務需要向表中插入新行,它會在需要插入的索引鍵上獲得一個排他鎖(Exclusive Lock)。如果該事務需要向表中插入新行但沒有指定索引鍵,則會在表上獲得一個排他鎖。

      Insert Intention Lock的作用是防止多個事務同時向同一索引鍵插入新行,從而保證數據的一致性和完整性。它只會在需要插入新行時才會被創建,不會對已經存在的行造成影響。需要注意的是,Insert Intention Lock只保護對于同一索引鍵的插入操作,對于不同索引鍵的插入操作沒有任何保護作用。

      參考文章:

      《MySQL是怎樣運行的-小孩子4919著》

      MySQL :: MySQL 5.7 Reference Manual :: 14.7.1 InnoDB Locking

      MySQL詳解--鎖.md (xuzhongcn.github.io)

      理解Mysql InnoDB引擎中的鎖 | Bigbyto (wiyi.org)

      Innodb間隙鎖實戰 - 掘金 (juejin.cn)

      一分鐘了解Mysql的間隙鎖——《深究Mysql鎖》_mysql間隙鎖_bug師姐的博客-CSDN博客

      posted @ 2023-04-08 15:37  聽到微笑  閱讀(36)  評論(0)    收藏  舉報  來源
      主站蜘蛛池模板: 国产精品中文字幕二区| 欧洲美熟女乱又伦免费视频 | 国产人妻精品午夜福利免费| 国产av一区二区三区精品| 光棍天堂在线手机播放免费| 欧美日韩中文国产一区| 欧美人与动交视频在线观看| 久天啪天天久久99久孕妇| 亚洲精品男男一区二区| 欧美高清freexxxx性| 玩弄放荡人妻少妇系列| 国产精品国产三级国产午| 国产91午夜福利精品| 人妻中文字幕亚洲精品| 日韩一卡二卡三卡四卡五卡| 国产一区二区三区小说| 国产无码高清视频不卡| 深田えいみ禁欲后被隔壁人妻| 2021国产精品一卡2卡三卡4卡| 成全高清在线播放电视剧| 亚洲天堂av 在线| 午夜福利精品国产二区| 麻豆蜜桃av蜜臀av色欲av| 久久国产精品波多野结衣| 宫西光有码视频中文字幕| 国产99视频精品免费视频76| 无码h片在线观看网站| 欧美丰满熟妇xxxx性| 汝州市| 色综合天天色综合久久网| 中国女人熟毛茸茸A毛片| 无码人妻精品丰满熟妇区| 色二av手机版在线| 久9视频这里只有精品| 成人精品一区日本无码网| 国产不卡的一区二区三区| 亚洲成av人片天堂网老年人| 国产精品无码不卡在线播放| 成人永久免费A∨一级在线播放| 99久久亚洲综合精品成人网| 亚洲第一区二区三区av|