理解MySQL事務和鎖的關鍵知識點整理

一致性視圖:

begin/start transaction 命令並不是一個事務的起點,在執行到它們之後的第一個操作 InnoDB 表的語句,事務才真正啓動。如果你想要馬上啓動一個事務,可以使用 start transaction with consistent snapshot 這個命令。

第一種啓動方式,一致性視圖是在第執行第一個快照讀語句時創建的;
第二種啓動方式,一致性視圖是在執行 start transaction with consistent snapshot 時創建的。

快照讀是基於 MVCC 和 undo log 來實現的,適用於簡單 select 語句。

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

更新數據時,都是先讀後寫,而這個讀,就是當前讀。讀取數據時,讀取該條數據的已經提交的最新的事務,生成的 readView。

如果把事務 A 的查詢語句 select * from t where id=1 修改一下,加上 lock in share mode 或 for update,也都可以讀到版本號是 101 的數據,返回的 k 的值是 3。下面這兩個 select 語句,就是分別加了讀鎖(S 鎖,共享鎖)和寫鎖(X 鎖,排他鎖)。

mysql> select k from t where id=1 lock in share mode;
mysql> select k from t where id=1 for update;

在 MySQL 裏,有兩個“視圖”的概念:

  • 一個是 view。它是一個用查詢語句定義的虛擬表,在調用的時候執行查詢語句並生成結果。創建視圖的語法是 create view … ,而它的查詢方法與表一樣。
  • 另一個是 InnoDB 在實現 MVCC 時用到的一致性讀視圖,即 consistent read view,用於支持 RC(Read Committed,讀提交)和 RR(Repeatable Read,可重複讀)隔離級別的實現。

ReadView中主要包含4個比較重要的內容:

  • m_ids:表示在生成ReadView時當前系統中活躍的讀寫事務的事務id列表。
  • min_trx_id:表示在生成ReadView時當前系統中活躍的讀寫事務中最小的事務id,也就是m_ids中的最小值。
  • max_trx_id:表示生成ReadView時系統中應該分配給下一個事務的id值。
  • creator_trx_id:表示生成該ReadView的事務的事務id。

InnoDB 裏面每個事務有一個唯一的事務 ID,叫作 transaction id。它是在事務開始的時候向 InnoDB 的事務系統申請的,是按申請順序嚴格遞增的。

而每行數據也都是有多個版本的。每次事務更新數據的時候,都會生成一個新的數據版本,並且把 transaction id 賦值給這個數據版本的事務 ID,記爲 row trx_id。同時,舊的數據版本要保留,並且在新的數據版本中,能夠有信息可以直接拿到它。

也就是說,數據表中的一行記錄,其實可能有多個版本 (row),每個版本有自己的 row trx_id。

 個人總結:

1.A啓動事務後,會創建一致性視圖.如果是快照讀(就是普通select語句).對其他事務的更新不可見.原理是會根據最新事務ID找到與當前事務ID一致的數據.所以還是舊的版本數據.

當然另外一種情況是,最新的事務id與當前事務ID一致,說明就是當前事務更新了數據.也可見

如果是當前讀(update之類語句),就會讀取當前行的最新數據.在此基礎上更新數據.如果當前的記錄的行鎖被其他事務佔用的話,就需要進入鎖等待。

2.事務的可重複讀的能力是怎麼實現的?

可重複讀的核心就是一致性讀(consistent read);而事務更新數據的時候,只能用當前讀。如果當前的記錄的行鎖被其他事務佔用的話,就需要進入鎖等待。

而讀提交的邏輯和可重複讀的邏輯類似,它們最主要的區別是:

  • 在可重複讀隔離級別下,只需要在事務開始的時候創建一致性視圖,之後事務裏的其他查詢都共用這個一致性視圖;
  • 在讀提交隔離級別下,每一個語句執行前都會重新算出一個新的視圖。

全局鎖表鎖和行鎖:

全局鎖就是對整個數據庫實例加鎖。可以通過命令 Flush tables with read lock (FTWRL) 對全局進行加鎖,讓整個庫處於只讀狀態。

MySQL裏面表級別的鎖有兩種:一種是表鎖,一種是元數據鎖(meta data lock,MDL)。

表鎖的語法是 lock tables ... read/write。與 FTWRL 類似,可以用 unlock tables 主動釋放鎖,也可以在客戶端斷開的時候自動釋放。需要注意的是,lock tables 語法除了會限制別的現成的讀寫外,也限定了本線程接下來的操作對象。

另一類表級鎖MDL不需要顯式使用。在訪問一個表的時候會自動加上。它的作用是保證讀寫的正確性。MDL是在MySQL5.5引入的,當對一個表做增刪改查操作的時候,加MDL讀鎖;當要對錶結構變更時,加MDL寫鎖。

在 InnoDB 事務中,行鎖是在需要的時候才加上的,但並不是不需要了就立刻釋放,而是要等到事務結束時才釋放。這個就是兩階段鎖協議。

當出現死鎖以後,有兩種策略:

直接進入等待,直到超時。這個超時時間可以通過參數 innodb_lock_wait_timeout 來設置。
發起死鎖檢測,發現死鎖後,主動回滾死鎖鏈條中的某一個事務,讓其他事務得以繼續執行。將參數 innodb_deadlock_detect 設置爲 on,表示開啓這個邏輯。
在 InnoDB 中,innodb_lock_wait_timeout 的默認值是 50s,意味着如果採用第一個策略,當出現死鎖以後,第一個被鎖住的線程要過 50s 纔會超時退出,然後其他線程纔有可能繼續執行。對於在線服務來說,這個等待時間往往是無法接受的。

所以,正常情況下我們還是要採用第二種策略,即:主動死鎖檢測,而且 innodb_deadlock_detect 的默認值本身就是 on。主動死鎖檢測在發生死鎖的時候,是能夠快速發現並進行處理的,但是它也是有額外負擔的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章