1. 程式人生 > 其它 >4.mysql鎖機制

4.mysql鎖機制

零、mysql鎖機制

  • 資料庫鎖機制簡單來說,就是資料庫為了保證資料的一致性,使各種共享資源在被訪問時變得有序而設計的一種規則!

  • MysQL的鎖機制比較簡單最著的特點是不同的儲存引擎支援不同的鎖機制。InoDB支援行鎖(有時也會升級為表鎖);MyISAM只支援表鎖

行鎖:鎖資料的一行,開銷小、加鎖快、不會出現死鎖。鎖粒度大,發生鎖衝突的概率小,併發相對較低。

表鎖:鎖整個表,開銷大、加鎖慢、會出現死鎖。鎖粒度小,發生鎖衝突的概率高,併發相對較高

一、InnoDB行鎖的種類

InnoDB預設的事務隔離級別是RR(可重複讀),並且引數innob_locks_unsafe_for_binling=0的模式下,行鎖有三種。

1.1、記錄鎖、(Record Lock)

把這條記錄加鎖:記錄鎖

行鎖是加在索引上的,有索引,行鎖才會生效。

測試:不加索引,兩個事務修改同一條記錄

測試:不加索引,兩個事務修改不同行的記錄

測試:加索引,修改同一行記錄

測試:加索引,修改不同行的記錄

1.2、間隙鎖(GAP Lock)

在RR這個級別下,為了避免幻讀,引入了間隙鎖,他鎖定的是記錄範圍,不包含記錄本身,也就是不允許在範圍內插資料。

間隙:根據檢索條件向下尋找最靠近檢索條件的記錄值A作為左區間,向上尋找最靠近檢索條件的記錄值B作為右區間,即鎖定的間隙為(A,B)。

注意:唯一索引 等值判斷只會產生記錄鎖,範圍查詢會產生間隙鎖

注意:非唯一索引 等值判斷也會產生間隙鎖,因為查出來可能會有多條資料

1.3、記錄鎖和間隙鎖的組合(next-key-lock)

臨鍵鎖,是記錄鎖與間隙鎖的組合,它的封鎖範圍,既包含索引記錄,又包含索引區間。

注:臨鍵鎖的主要目的,也是為了避免幻讀(Phantom Read)。如果把事務的隔離級別降級為RC,臨鍵鎖則也會失效。

二、表鎖

1、對於InnoDB表, 在絕大部分情況下都應該使用行級鎖,因為事務和行鎖往往是我們之所以選擇InnoDB表的理由。但在個另特殊事務中,也可以考慮使用表級鎖。

  • 第一種情況是:事務需要更新大部分或全部資料,表又比較大,如果使用預設的行鎖,不僅這個事務執行效率低,而且可能造成其他事務長時間鎖等待和鎖衝突,這種情況下可以考慮使用表鎖來提高該事務的執行速度。(有300W資料,150W條需要修改,直接上表鎖,因為行鎖需要鎖150W次)

  • 第二種情況是:事務涉及多個表,比較複雜,很可能引起死鎖,j造成大量事務回滾。這種情況也可以考慮一次性鎖定事務涉及的表,從而避免死鎖、減少資料庫因事務回滾帶來的開銷。

三、InnoDB的鎖型別

3.1、讀鎖

讀鎖(共享鎖,shared lock)簡稱S鎖。一個事務獲取了一個數據行的讀鎖,其他事務能獲得該行對應的讀鎖但不能獲得寫鎖,即一個事務在讀取一個數據行時,其他事務也可以讀,但不能對該數行增刪改的操作。(簡而言之:多個事務可以讀,只能一個事務寫)

讀鎖有兩種select方式的應用:

1.第一種是自動提交模式下的select查詢語句,不需加任何鎖,直接返回查詢結果,這就是一致性非鎖定讀。

⒉第二種就是通過select.lock in share mode被讀取的行記錄或行記錄的範圍上加一個讀鎖讓其他事務可以讀,但是要想申請加寫鎖那就會被阻塞。

3.2、寫鎖

寫鎖,也叫排他鎖,或者叫獨佔所,簡稱x鎖。一個事務獲取了一個數據行的寫鎖,其他事務就不能再獲取該行的其他鎖與鎖優先順序最高。(簡而言之:就是隻能有一個事務操作這個資料,別的事務不能同時和它操作)

3.3、MDL鎖(meta data lock)

  • 什麼叫元資料:表結構資訊;

開啟了查詢事務後就會預設上一個MDL鎖,上了 MDL 鎖 ,就不能修改表結構。

3.4、意向鎖

在mysql的innob引擎中,意向鎖是表級鎖,意向鎖有兩種:

  • 意向共享鎖(IS)是指在給一個數據行加共享鎖前必須獲取該表的意向共享鎖(維護表結構)

  • 意向排它鎖(IX)是指在給一個數據行加排他鎖前必須獲取該表的意向排他鎖

  • 意向鎖和MIDL鎖都是為了防止在事務進行中,執行DDL語句導致資料不一致。

四、從另一個角度區分鎖的分配

4.1、樂觀鎖(需要自己手動實現的)

樂觀鎖大多是基於資料版本記錄機制實現,一般是給資料庫表增加一個"version"欄位。讀取資料時,將此版本號一同讀出,之後更新時,對此版本號加一。此時將提交資料的版本資料與資料庫表對應記錄的當前版本資訊進行比對,如果提交的資料版本號大於資料庫表當前版本號,則予以更新,否則認為是過期資料。

4.2、悲觀鎖(前面講的鎖都是悲觀鎖)

不保護必出問題。

悲觀鎖依靠資料庫提供的鎖機制實現。MySQL中的共享鎖和排它鎖都是悲觀鎖。資料庫的增刪改操作預設都會加排他鎖,而查詢不會加任何鎖。此處不贅述。

五、鎖等待和死鎖

5.1、鎖等待:

一個事務加鎖修改表資料,鎖表,另一個事務得等待:鎖等待;
可以設定鎖等待時間的,超過的話,就會自動結束。預設50S,

5.2、死鎖:

死鎖是指兩個或兩個以上的程序在執行過程中,因爭奪資源而造成的一種互相等待的現象,就是所謂的鎖資源請求產生了迴路現象,即死迴圈。

InnoDB引擎可以自動檢測死鎖並回滾該事務好不容易執行了一個業務給我回滾了,所以死鎖儘量不要出現。

六、如何避免死鎖

  • 出現死鎖不可怕,但我們要儘量避免死鎖

  • 如果不同的程式會併發的處理同一張表,或者涉及表中多行記錄,儘量約定使用相同順序訪問表,可以大大減少死鎖概率的發生

  • 業務中採用小事務,避免使用大事務,要及時提交和回滾事務,可以減少死鎖的發生

  • 同一個事務中儘量做到一次鎖定所需要的所有資源(表鎖),減少死鎖發生的概率。

  • 對於非常容易發生死鎖的業務,可以嘗試使用升級鎖的力度,該用表鎖減少死鎖的發生

七、鎖總結

行鎖,記錄鎖,間隙鎖,臨建鎖。
mdl鎖 > 查詢資料一開始,不能修改表結構
意向鎖 > 獲取鎖前要先獲取意向鎖,不能修改表結構
樂觀鎖 > 無鎖,version控制
悲觀鎖 > mysql都是悲觀鎖 上鎖
讀鎖(共享鎖、S鎖),
寫鎖(獨佔鎖、排它鎖、互斥鎖、X鎖)

八、多版本併發控制(MVCC)

MVCC(Mutil-Version Concurrency Control),就是多版本併發控制。MVCC 是一種併發控制的方法,一般在資料庫管理系統中,實現對資料庫的併發訪問。

重要:版本鏈

8.1、什麼是當前讀和快照讀?

8.1.1、當前讀

當前讀,讀取的是記錄的最新版本,並且,當前讀返回的記錄,都會加上鎖,保證其他事務不會再併發修改這條記錄。

當前讀是基於 臨鍵鎖(行鎖 + 間歇鎖)來實現的,適用於 insert,update,delete, select ... for update, select ... lock in share mode 語句,以及加鎖了的 select 語句。

8.2.2、快照讀

快照讀,讀取的是快照資料,不加鎖的簡單 Select 都屬於快照讀.
在Read Committed和Repeatable Read隔離級別下,普通的SELECT查詢都是讀取MVCC版本鏈中的一個版本,相當於讀取一個快照,因此稱為快照讀。這種讀取方式不會加鎖,因此讀操作時非阻塞的,因此也叫非阻塞讀。

在標準的Repeatable Read隔離級別下讀操作會加S鎖,直到事務結束,因此可以阻止其他事務的寫操作;但在MySQL的Repeatable Read隔離級別下讀操作沒有加鎖,不會阻止其他事務對相同記錄的寫操作,因此在後續進行寫操作時就有可能寫入基於版本鏈中的舊資料計算得到的結果,這就導致了提交覆蓋的問題。想要避免此問題,就需要另外加鎖來實現。

8.2、當前讀、快照讀和MVCC的關係

  • 準確的說,MVCC多版本併發控制指的是“維護一個數據的多個版本,使得讀-寫操作沒有衝突”這麼一個概念,僅僅是一個理想概念。

  • 而在mysql中,實現這麼一個理想概念,我們就需要mysql提供具體的功能去實現它,而快照讀就是mysql為我們實現MVCC理想模型中的一個非阻塞式讀功能。而相對而言,當前讀就是悲觀鎖的具體功能實現

8.3、MVCC解決什麼問題

** 資料庫併發有三種場景,分別是:**

  • 讀-讀:不存線上程安全,也不需要版本控制
  • 讀-寫:有執行緒安全,可能會造成事務的隔離性的問題,可能造成髒讀、幻讀、不可重複讀。
  • 寫-寫:有執行緒安全問題,可能會存在更新丟失問題,比如第一類更新丟失或者第二類更新丟失。

** MVCC帶來的好處:**
MVCC是一種用來解決讀寫衝突無鎖版本控制,也就是為事務分配單項增長的時間戳,為每一次修改給定一個版本,並且通過指標去指向,讀操作只讀事務開始前的資料庫快照,所以MVCC可以為資料庫解決一下問題:

  • 讀-寫之間阻塞的問題,通過 MVCC 可以讓讀寫互相不阻塞,讀不相互阻塞,寫不阻塞讀,這樣可以提升資料併發處理能力。

  • 降低了死鎖的概率,這個是因為 MVCC 採用了樂觀鎖的方式,讀取資料時,不需要加鎖,寫操作,只需要鎖定必要的行。

  • 解決了一致性讀的問題,當我們朝向某個資料庫在時間點的快照是,只能看到這個時間點之前事務提交更新的結果,不能看到時間點之後事務提交的更新結果。

小結一下:
總之,MVCC就是因為大牛們,不滿意只讓資料庫採用悲觀鎖這樣效能不加的形式去解決讀寫衝突,而提出的解決方案,所以在資料庫中,因為有了MVCC,我們有了一下兩個組合:

  • MVCC + 悲觀鎖

MVCC用來解決讀-寫衝突,悲觀鎖用來解決 寫-寫衝突

  • MVCC + 樂觀鎖

MVCC用來解決讀-寫衝突,樂觀鎖用來解決 寫-寫衝突

這種組合的方式就可以最大程度的提高資料庫併發效能,並解決讀寫衝突,和寫寫衝突導致的問題

8.4、MVCC實現原理

MVCC的目的就是多版本併發控制,在資料庫中的實現,就是為了解決讀寫衝突,它的實現原理主要是依賴記錄中的3個隱式欄位undo日誌(回滾記錄日誌),Read View來實現的
。所以我們先來看看這個三個point的概念

8.4.1、3個隱式欄位

8.4.2、用途

8.4.2、undo日誌

記錄舊的資料鏈,版本選擇,幫助我們實現快照讀, 實現回滾,如上圖↑

8.5、Read View(讀檢視)

什麼是Read View,說白了Read View就是事務進行快照讀操作的時候生產的讀檢視(Read View),在該事務執行的快照讀的那一刻,會生成資料庫系統當前的一個快照,記錄並維護系統當前活躍事務的ID(當每個事務開啟時,都會被分配一個ID, 這個ID是遞增的,所以最新的事務,ID值越大)

九、Redo log 日誌

記錄狀態的:
比如 我把一些快取中資料儲存到磁盤裡面了,突然停電啦,我的資料就沒有啦嗎?不是的,首先我們會先把資料寫到redo log日誌裡面,然後再去修改緩衝池裡面的資料,最後在寫會磁碟,記錄我那時候的狀態,用於恢復。
不恰當的比喻>>>>>>>>>
將資料載入到記憶體進行修改,修改完之後不立馬刷回到磁碟,先讚一讚,如果此時電腦重啟了,這時就需要redo log日誌,來處理了,因為它記錄了狀態。

9.1、redo log 工作原理

說白了,redo log就是儲存了資料被修改後的值。當我們提交一個事務時,lnnoDB會先去把要修改的資料寫入日誌,然後再去修改緩衝池裡面的真正資料頁。

我們著重看看redo log是怎麼一步步寫入磁碟的。redo log本身也由兩部分所構成即重做日誌緩衝(redo log buffer、記憶體)重做日誌檔案(redolog file、磁碟)。這樣的設計同樣也是為了調和記憶體與磁碟的速度差異。InnoDB寫入磁碟的策略可以通過innodb_f lush_log_at_trx_commit這個引數來控制。

9.2、宕機恢復

DB宕機後重啟,InnoDB會首先去檢視資料頁中的LSN的數值。這個值代表資料頁被重新整理回磁碟的LSN的大小。然後再去檢視redo log的LSN的大小。如果資料頁中的LSN值大說明資料頁領先於redo log重新整理回磁碟,不需要進行恢復。反之需要從redo log中恢復資料。