一致性視圖:
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。主動死鎖檢測在發生死鎖的時候,是能夠快速發現並進行處理的,但是它也是有額外負擔的。