首頁 > 軟體

一文搞懂Mysql中的共用鎖、排他鎖、悲觀鎖、樂觀鎖及使用場景

2022-07-04 14:03:16

一、常見鎖型別

  • 表級鎖,鎖定整張表
  • 頁級鎖,鎖定一頁
  • 行級鎖,鎖定一行
  • 共用鎖,也叫S鎖,在MyISAM中也叫讀鎖
  • 排他鎖,也叫X鎖,在MyISAM中也叫寫鎖
  • 悲觀鎖,抽象性質,其實不真實存在
  • 樂觀鎖,抽象性質,其實不真實存在

常見鎖型別

二、Mysql引擎介紹

  • 其實mysql中的引擎有很多種類,其中InnoDB和MyISAM引擎最常用
  • 在mysql5.5版本前預設使用MyISAM引擎,之後使用InnoDB引擎
  • 檢視資料庫引擎命令如下
show variables like '%storage_engine%';

三、常用引擎間的區別 

  • MyISAM 運算元據都是使用的表鎖,你更新一條記錄就要鎖整個表,導致效能較低,並行不高。當然同時它也不會存在死鎖問題。
  • 而 InnoDB 與 MyISAM 的最大不同有兩點:一是 InnoDB 支援事務;二是 InnoDB 採用了行級鎖。
  • 在 Mysql 中,行級鎖並不是直接鎖記錄,而是鎖索引。索引分為主鍵索引和非主鍵索引兩種,如果一條sql 語句操作了主鍵索引,Mysql 就會鎖定這條主鍵索引;如果一條語句操作了非主鍵索引,MySQL會先鎖定該非主鍵索引,再鎖定相關的主鍵索引。
  • InnoDB 行鎖是通過給索引項加鎖實現的,如果沒有索引,InnoDB 會通過隱藏的聚簇索引來對記錄加鎖。也就是說:如果不通過索引條件檢索資料,那麼InnoDB將對錶中所有資料加鎖,實際效果跟表鎖一樣。因為沒有了索引,找到某一條記錄就得掃描全表,要掃描全表,就得鎖定表。

四、共用鎖與排他鎖

資料庫的增刪改操作預設都會加排他鎖,而查詢不會加任何鎖。

共用鎖:對某一資源加共用鎖,自身可以讀該資源,其他人也可以讀該資源(也可以再繼續加共用鎖,即 共用鎖可多個共存),但無法修改。要想修改就必須等所有共用鎖都釋放完之後。

排他鎖:對某一資源加排他鎖,自身可以進行增刪改查,其他人無法進行任何操作。

//共用鎖
select * from 表名 lock in share mode
 
//排他鎖
select * from 表名 for update

五、排他鎖的實際應用

  • 這裡我們以兩個運算元據庫的請求為例,假設這兩個請求分別為T1和T2
  • 假設T1為查詢請求,而T2為更新資料請求,在T1查詢很長時間的時候,還沒有返回結果,但是這時候T2過來請求更新了
  • 這個流程應該是: T1執行加共用鎖、T2執行、發現T1未完成等待其完成、T1完成、T2開始執行
  • T2之所以要等待,是因為T2執行更新的時候需要給表加排他鎖,但是資料庫規定,不能在同一資源上同時共存這兩種鎖,所以T2必須等T1執行完,釋放鎖後,才可以正常操作
T1: select * from 表名 lock in share mode //假設還未返回結果
 
T2: update 表名 set name='autofelix'

六、共用鎖的實際應用

  • 如果T1和T2都是執行的查詢,也就是都加共用鎖
  • 這時候就不用等待,可以立馬執行
  • 因為同一資源上可以同時存在多個共用鎖,也被稱為,共用鎖與共用鎖相容
  • 意味著共用鎖不阻止其他人同時讀取資源,但是阻止其他人修改資源
T1: select * from table lock in share mode
 
T2: select * from table lock in share mode

七、死鎖的發生

  • 假設T1和T2都同時執行2個資源操作,分別是查詢和更新資料
  • 假設T1和T2同時達到select,T1對錶加共用鎖,而T2也加上了共用鎖
  • 當T1的select執行完畢,準備執行update時
  • 根據鎖機制,T1的共用鎖必須升級到排他鎖才可以執行接下來的update操作
  • 在升級排他鎖之前,必須等T2的共用鎖釋放,同理,T2也在等T1的共用鎖釋放
  • 於是都在等待對方的鎖釋放,導致程式卡死,這種情況就是死鎖
T1: 開啟事務,執行查詢更新兩個操作
 
     select * from table lock in share mode
 
     update table set column1='hello'
 
T2: 開啟事務,執行查詢更新兩個操作
 
     select * from table lock in share mode
 
     update table set column1='world'

八、另一種發生死鎖的情景

  • 當T1和T2都是隻執行更新語句的時候
  • 如下程式所示,這種語句非常的常見,很多人覺得他會產生死鎖,其實要看情況
  • 如果id是主鍵,由於主鍵機制,並不需要全表掃描,直接可以更新當前資料,所以不會產生死鎖
  • 如果id是普通欄位,那麼當T1加上排他鎖之後,T2為了找到id=20條資料,必須進行全表掃描,當他掃到第10條的時候,發現這裡有排他鎖,導致全表掃描進行不下去,就會導致等待
T1: begin
     update table set content='hello' where id=10
 
T2: begin
     update table set content='world' where id=20

九、死鎖的解決方式

  • 就是讓T1和T2順序執行,比如T1在執行完select後,立馬給自身加上排他鎖,這樣T2不得不等待T1執行完才能繼續
  • 但是如果有很多請求過來的話,都必須等待,這對使用者特別的不友好
  • 所以,某些資料庫引入了另一種方式,叫做更新鎖,這裡mysql除外,不存在更新鎖
  • 更新鎖其實就是排他鎖的另一種實現,只是他允許其他人讀的同時加共用鎖,但是不允許其他操作,除非釋放了更新鎖
  • 流程大概如此: T1執行完select加上更新鎖,T2執行查詢完,準備加更新鎖,發現已經有了,就等待,其他請求過來,如果查詢是不受影響的,但是更新才等待
  • 這相比上面的查詢也要等待增加了效率
T1: begin
 
     select * from table for update
 
     update table set content='hello'
 
T2: begin
 
     select * from table for update
 
     update table set content='world'
T1: begin
 
     select * from table [加更新鎖操作]
 
     update table set content='hello'
 
T2: begin
 
     select * from table [加更新鎖操作]
 
     update table set content='world'

十、意向鎖和計劃鎖

  • 計劃鎖與程式猿無關,不需要了解
  • 意向鎖,Innodb特有,分為意向共用鎖和意向排他鎖
  • 意向共用鎖: 表示事務獲取共用鎖時,必須先得獲取該表的意向共用鎖
  • 意向排他鎖: 表示事務獲取排他鎖時,必須先得獲取該表的意向排他鎖
  • 我們知道要對整個表加鎖,必須保證表內不存在任何鎖
  • 如果一行行的去檢查是否加鎖,效率必然極低,這時候可以檢測意向鎖是否被佔用即可

十一、樂觀鎖和悲觀鎖

  • 樂觀鎖和悲觀鎖都是針對select而言的
  • 比如在商品搶購中,使用者購買後庫存需要減1,而很多使用者同時購買時,讀出來的庫存數量一樣,然後多個使用者同時用該庫存去減1
  • 這種做法必然會出現很大的漏洞,如果向在淘寶,京東出現這種情況,你就可以打包回家種地了
  • 這種情況如何解決呢,其實可以使用悲觀鎖進行解決,說白了也就是排他鎖
  • 使用者進來查庫存的時候,就加上排他鎖,等他所有操作完成後,再釋放排他鎖,讓其他人進來
  • 不讓使用者等待,就可以使用樂觀鎖方式解決,樂觀鎖一般靠表的設計和時間戳來實現
  • 一般是在表中新增version或者timestamp時間戳欄位
  • 這樣就會保證如果更新失敗,就表示有其他程式更新了資料庫,就可以通過重試解決
update table set num=num-1 where id=10 and version=12 

總結

到此這篇關於一文搞懂Mysql中共用鎖、排他鎖、悲觀鎖、樂觀鎖及使用場景的文章就介紹到這了,更多相關Mysql共用鎖、排他鎖、悲觀鎖、樂觀鎖內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com