溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

MySQL數(shù)據(jù)庫的鎖機制是什么

發(fā)布時間:2022-01-04 17:00:32 來源:億速云 閱讀:164 作者:iii 欄目:MySQL數(shù)據(jù)庫

本篇內容介紹了“MySQL數(shù)據(jù)庫的鎖機制是什么”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!

為什么要加鎖?

問題背景

  • 當多個用戶并發(fā)地存取數(shù)據(jù)時,在數(shù)據(jù)庫中就會產(chǎn)生多個事務同時存取同一數(shù)據(jù)的情況。若對并發(fā)操作不加控制就可能會讀取和存儲不正確的數(shù)據(jù),破壞數(shù)據(jù)庫的一致性。

要解決的問題

  • 多用戶環(huán)境下保證數(shù)據(jù)庫完整性和一致性

鎖是什么

  • 在計算機科學中,鎖是在執(zhí)行多線程時用于強行限制資源訪問的同步機制,即用于在并發(fā)控制中保證對互斥要求的滿足。

  • 加鎖是實現(xiàn)數(shù)據(jù)庫并發(fā)控制的一個非常重要的技術。當事務在對某個數(shù)據(jù)對象進行操作前,先向系統(tǒng)發(fā)出請求,對其加鎖。加鎖后事務就對該數(shù)據(jù)對象有了一定的控制,在該事務釋放鎖之前,其他的事務不能對此數(shù)據(jù)對象進行更新操作。

鎖的分類

  • 行級鎖

  • 行級鎖是Mysql中鎖定粒度最細的一種鎖,表示只針對當前操作的行進行加鎖。行級鎖能大大減少數(shù)據(jù)庫操作的沖突。其加鎖粒度最小,但加鎖的開銷也最大。行級鎖分為共享鎖 和 排他鎖。

  • 特點

  • 開銷D,加鎖慢;會出現(xiàn)死鎖;鎖定粒度最小,F(xiàn)生鎖沖突的概率最低,并F度也最高。

  • 表級鎖是MySQL中鎖定粒度最大的一種鎖,表示對當前操作的整張表加鎖,它實現(xiàn)簡單,資源消耗較少,被大部分MySQL引擎支持。最常使用的MYISAM與INNODB都支持表級鎖定。表級鎖定分為表共享讀鎖(共享鎖)與表獨占寫鎖(排他鎖)。

  • 特點

  • 開銷小,加鎖快;不會出現(xiàn)死鎖;鎖定粒度D,F(xiàn)出鎖沖突的概率最高,并發(fā)度最低。

  • 頁級鎖是MySQL中鎖定粒度介于行級鎖和表級鎖中間的一種鎖。表級鎖速度快,但沖突多,行級沖突少,但速度慢。所以取了折衷的頁級,一次鎖定相鄰的一組記錄。BDB支持頁級鎖

  • 特點

  • 開銷和加鎖時間界于表鎖和行鎖之間;會出現(xiàn)死鎖;鎖定粒度界于表鎖和行鎖之間,并發(fā)度一般

MySQL常用存儲引擎的鎖機制

MyISAM和MEMORY采用表級鎖(table-level locking)

BDB采用頁面鎖(page-level locking)或表級鎖,默認為頁面鎖

InnoDB支持行級鎖(row-level locking)和表級鎖,默認為行級鎖

  • InnoDB行鎖是通過給索引上的索引項加鎖來實現(xiàn)的,InnoDB這種行鎖實現(xiàn)特點意味著:只有通過索引條件檢索數(shù)據(jù),InnoDB才使用行級鎖,否則,InnoDB將使用表鎖!

在實際應用中,要特別注意InnoDB行鎖的這一特性,不然的話,可能導致大量的鎖沖突,從而影響并發(fā)性能。

行級鎖都是基于索引的,如果一條SQL語句用不到索引是不會使用行級鎖的,會使用表級鎖。行級鎖的缺點是:由于需要請求大量的鎖資源,所以速度慢,內存消耗大。

實例說明

  • MySQL InnoDB引擎默認的修改數(shù)據(jù)語句:update,delete,insert都會自動給涉及到的數(shù)據(jù)加上排他鎖。

select語句默認不會加任何鎖類型,如果加排他鎖可以使用select …for update語句,加共享鎖可以使用select … lock in share mode語句。

所以加過排他鎖的數(shù)據(jù)行在其他事務種是不能修改數(shù)據(jù)的,也不能通過for update和lock in share mode鎖的方式查詢數(shù)據(jù),但可以直接通過select …from…查詢數(shù)據(jù),因為普通查詢沒有任何鎖機制。

行級鎖與死鎖

MyISAM中是不會產(chǎn)生死鎖的,因為MyISAM總是一次性獲得所需的全部鎖,要么全部滿足,要么全部等待。而在InnoDB中,鎖是逐步獲得的,就造成了死鎖的可能。

在MySQL中,行級鎖并不是直接鎖記錄,而是鎖索引。索引分為主鍵索引和非主鍵索引兩種,如果一條sql語句操作了主鍵索引,MySQL就會鎖定這條主鍵索引;如果一條語句操作了非主鍵索引,MySQL會先鎖定該非主鍵索引,再鎖定相關的主鍵索引。在UPDATE、DELETE操作時,MySQL不僅鎖定WHERE條件掃描過的所有索引記錄,而且會鎖定相鄰的鍵值,即所謂的next-key locking。

當兩個事務同時執(zhí)行,一個鎖住了主鍵索引,在等待其他相關索引。另一個鎖定了非主鍵索引,在等待主鍵索引。這樣就會發(fā)生死鎖。

發(fā)生死鎖后,InnoDB一般都可以檢測到,并使一個事務釋放鎖回退,另一個獲取鎖完成事務。

共享鎖與排它鎖

共享鎖(Share Lock)

  • 共享鎖又稱讀鎖,是讀取操作創(chuàng)建的鎖。其他用戶可以并發(fā)讀取數(shù)據(jù),但任何事務都不能對數(shù)據(jù)進行修改(獲取數(shù)據(jù)上的排他鎖),直到已釋放所有共享鎖。

如果事務T對數(shù)據(jù)A加上共享鎖后,則其他事務只能對A再加共享鎖,不能加排他鎖。獲準共享鎖的事務只能讀數(shù)據(jù),不能修改數(shù)據(jù)。

用法 SELECT ... LOCK IN SHARE MODE;

在查詢語句后面增加LOCK IN SHARE MODE,Mysql會對查詢結果中的每行都加共享鎖,當沒有其他線程對查詢結果集中的任何一行使用排他鎖時,可以成功申請共享鎖,否則會被阻塞。其他線程也可以讀取使用了共享鎖的表,而且這些線程讀取的是同一個版本的數(shù)據(jù)。

排它鎖(eXclusive Lock)

  • 排他鎖又稱寫鎖,如果事務T對數(shù)據(jù)A加上排他鎖后,則其他事務不能再對A加任任何類型的封鎖。獲準排他鎖的事務既能讀數(shù)據(jù),又能修改數(shù)據(jù)。

用法 SELECT ... FOR UPDATE;

在查詢語句后面增加FOR UPDATE,Mysql會對查詢結果中的每行都加排他鎖,當沒有其他線程對查詢結果集中的任何一行使用排他鎖時,可以成功申請排他鎖,否則會被阻塞。

樂觀鎖(Optimistic Lock)

是什么

  • 假設認為數(shù)據(jù)一般情況下不會造成沖突,所以在數(shù)據(jù)進行提交更新的時候,才會正式對數(shù)據(jù)的沖突與否進行檢測,如果發(fā)現(xiàn)沖突了,則讓返回用戶錯誤的信息,讓用戶決定如何去做。

相對于悲觀鎖,在對數(shù)據(jù)庫進行處理的時候,樂觀鎖并不會使用數(shù)據(jù)庫提供的鎖機制。一般的實現(xiàn)樂觀鎖的方式就是記錄數(shù)據(jù)版本。

數(shù)據(jù)版本,為數(shù)據(jù)增加的一個版本標識。當讀取數(shù)據(jù)時,將版本標識的值一同讀出,數(shù)據(jù)每更新一次,同時對版本標識進行更新。當我們提交更新的時候,判斷數(shù)據(jù)庫表對應記錄的當前版本信息與第一次取出來的版本標識進行比對,如果數(shù)據(jù)庫表當前版本號與第一次取出來的版本標識值相等,則予以更新,否則認為是過期數(shù)據(jù)。

實現(xiàn)數(shù)據(jù)版本有兩種方式,第一種是使用版本號,第二種是使用時間戳。

使用版本號實現(xiàn)樂觀鎖

  • 使用版本號時,可以在數(shù)據(jù)初始化時指定一個版本號,每次對數(shù)據(jù)的更新操作都對版本號執(zhí)行+1操作。并判斷當前版本號是不是該數(shù)據(jù)的最新的版本號。

1.查詢出商品信息
select (status,status,version) from t_goods where id=#{id}
2.根據(jù)商品信息生成訂單
3.修改商品status為2
update t_goods
set status=2,version=version+1
where id=#{id} and version=#{version};

優(yōu)點與不足

  • 樂觀并發(fā)控制相信事務之間的數(shù)據(jù)競爭(data race)的概率是比較小的,因此盡可能做下去,直到提交的時候才去鎖定,所以不會產(chǎn)生任何鎖和死鎖。但如果直接簡單這么做,還是有可能會遇到不可預期的結果,例如兩個事務都讀取了數(shù)據(jù)庫的某一行,經(jīng)過修改以后寫回數(shù)據(jù)庫,這時就遇到了問題

悲觀鎖(Pessimistic Lock)

是什么

  • 在整個數(shù)據(jù)處理過程中,將數(shù)據(jù)處于鎖定狀態(tài)。悲觀鎖的實現(xiàn),往往依靠數(shù)據(jù)庫提供的鎖機制 (也只有數(shù)據(jù)庫層提供的鎖機制才能真正保證數(shù)據(jù)訪問的排他性,否則,即使在本系統(tǒng)中實現(xiàn)了加鎖機制,也無法保證外部系統(tǒng)不會修改數(shù)據(jù))

悲觀鎖的流程

  • 在對任意記錄進行修改前,先嘗試為該記錄加上排他鎖(exclusive locking)。

  • 如果加鎖失敗,說明該記錄正在被修改,那么當前查詢可能要等待或者拋出異常。具體響應方式由開發(fā)者根據(jù)實際需要決定。

  • 如果成功加鎖,那么就可以對記錄做修改,事務完成后就會解鎖了。

  • 其間如果有其他對該記錄做修改或加排他鎖的操作,都會等待我們解鎖或直接拋出異常。

MySQL InnoDB中使用悲觀鎖

  • 要使用悲觀鎖,我們必須關閉mysql數(shù)據(jù)庫的自動提交屬性,因為MySQL默認使用autocommit模式,也就是說,當你執(zhí)行一個更新操作后,MySQL會立刻將結果進行提交。set autocommit=0;

//0.開始事務
begin;
//1.查詢出商品信息
select status from t_goods where id=1 for update;
//2.根據(jù)商品信息生成訂單
insert into t_orders (id,goods_id) values (null,1);
//3.修改商品status為2
update t_goods set status=2;
//4.提交事務
commit;

上面的查詢語句中,我們使用了select…for update的方式,這樣就通過開啟排他鎖的方式實現(xiàn)了悲觀鎖。此時在t_goods表中,id為1的 那條數(shù)據(jù)就被我們鎖定了,其它的事務必須等本次事務提交之后才能執(zhí)行。這樣我們可以保證當前的數(shù)據(jù)不會被其它事務修改。

Java的鎖機制

線程的同步問題

  • 一段synchronized的代碼被一個線程執(zhí)行之前,他要先拿到執(zhí)行這段代碼的權限,在java里邊就是拿到某個同步對象的鎖(一個對象只有一把鎖);如果這個時候同步對象的鎖被其他線程拿走了,他(這個線程)就只能等了(線程阻塞在鎖池等待隊列中)。取到鎖后,他就開始執(zhí)行同步代碼(被synchronized修飾的代碼);線程執(zhí)行完同步代碼后馬上就把鎖還給同步對象,其他在鎖池中等待的某個線程就可以拿到鎖執(zhí)行同步代碼了。這樣就保證了同步代碼在統(tǒng)一時刻只有一個線程在執(zhí)行。

線程的同步方法:

  • 1. 在需要同步的方法的方法簽名中加入synchronized關鍵字。

  • 2. 使用synchronized塊對需要進行同步的代碼段進行同步。

  • 3. 使用JDK 5中提供的java.util.concurrent.lock包中的Lock對象。

ThreadLocal

當使用ThreadLocal維護變量時,ThreadLocal為每個使用該變量的線程提供獨立的變量副本,所以每一個線程都可以獨立地改變自己的副本,而不會影響其它線程所對應的副本。在ThreadLocal類中有一個Map,用于存儲每一個線程的變量副本,Map中元素的鍵為線程對象,而值對應線程的變量副本。使用ThreadLocal的典型場景如數(shù)據(jù)庫連接管理,線程會話管理等場景,只適用于獨立變量副本的情況,如果變量為全局共享的,則不適用在高并發(fā)下使用。

“MySQL數(shù)據(jù)庫的鎖機制是什么”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關的知識可以關注億速云網(wǎng)站,小編將為大家輸出更多高質量的實用文章!

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內容。

AI