目錄
InnoDB架構
事務原理
MVCC
InnoDB架構
從MySQL5.5版本開始默認使用InnoDB存儲引擎,它擅長進行事務處理,具有崩潰恢復的特性,在日常開發中使用非常廣泛,其邏輯存儲結構圖如下所示,
下面是InnoDB架構圖,左側為內存結構,右 側為磁盤結構,如下所示:
內存結構:主要有以下幾部分組成,其特點如下所示:
1)Buffer Pool:緩沖池是主內存中的一個區域,里面可以緩存磁盤上經常操作的真實數據,在執行增刪改查操作時,先操作緩沖池中的數據(若緩沖池沒有數據,則從磁盤加載并緩存),然后再以一定頻率刷新到磁盤,從而減少磁盤I0,加快處理速度。
緩沖池:以Page頁為單位,底層采用鏈表數據結構管理Page,根據狀態可以將Page分為三種類型:
free page:空閑page,未被使用。·
cleanpage:被使用page,數據沒有被修改過。·
dirtypage:臟頁,被使用page,數據被修改過,也中數據與磁盤的數據產生了不一致。
2)ChangeBuffer:更改緩沖區(針對于非唯一二級索引頁),在執行DML語句時,如果這些數據Page沒有在BufferPool中,不會直接操作磁盤,而會將數據變更存在更改緩沖區ChangeBuffer中,在未來數據被讀取時,再將數據合并恢復到BufferPool中,再將合并后的數據刷新到磁盤中。與聚集索引不同,二級索引通常是非唯一的,并且以相對隨機的順序插入二級索引。同樣,刪除和更新可能會影響索引樹中不相鄰的二級索引頁,如果每一次都操作磁盤,會造成大量的磁盤IO。有了ChangeBuffer之后,我們可以在緩沖池中進行合并處理,減少磁盤lO。
3)AdaptiveHashIndex:自適應hash索引,用于優化對BufferPool數據的查詢。InnoDB存儲引擎會監控對表上各索引頁的查詢,如果觀察到hash索引可以提升速度,則建立hash索引l,稱之為自適應hash索引。自適應哈希索引,無需人工干預,是系統根據情況自動完成。參數:adaptive_hash_index
4)LogBuffer:日志緩沖區,用來保存要寫入到磁盤中的log日志數據(redolog、undolog),默認大小為16MB,日志緩沖區的日志會定期刷新到磁盤中。如果需要更新、插入或刪除許多行的事務,增加日志緩沖區的大小可以節省磁盤1/0。
參數:innodb_log_buffer_size:緩沖區大小
innodb_flush_log_at_trx_commit:日志刷新到磁盤時機
磁盤結構:主要有以下幾部分組成,其特點如下所示:
1)SystemTablespace:系統表空間是更改緩沖區的存儲區域。如果表是在系統表空間而不是每個表文件或通用表空間中創建的,它也可能包含表和索引數據。(在MySQL5.x版本中還包含lnnoDB數據字典、undolog等)參數:innodb_data_file_path
2)File-Per-TableTablespaces:每個表的文件表空間包含單個lnnoDB表的數據和索引,并存儲在文件系統上的單個數據文件中。參數:innodb_file_per_table
3)GeneralTablespaces:通用表空間,需要通過CREATETABLESPACE語法創建通用表空間,在創建表時,可以指定該表空間。
4)UndoTablespaces:撤銷表空間,MySQL實例在初始化時會自動創建兩個默認的undo表空間(初始大小16M),用于存儲undolog日志。
5)Temporary Tablespaces:InnoDB使用會話臨時表空間和全局臨時表空間。存儲用戶創建的臨時表等數據。
6)DoublewriteBufferFiles:雙寫緩沖區,innoDB引擎將數據頁從BufferPool刷新到磁盤前,先將數據頁寫入雙寫緩沖區文件中,便于系統異常時恢復數據。
7)RedoLog:重做日志,是用來實現事務的持久性。該日志文件由兩部分組成:重做日志緩沖(redologbuffer)以及重做日志文件(redolog),前者是在內存中,后者在磁盤中。當事務提交之后會把所有修改信息都會存到該日志中,用于在刷新臟頁到磁盤時,發生錯誤時,進行數據恢復使用。
后臺線程:InnoDB存儲引擎緩沖池中主要有四個后臺線程,它們主要內容如下:
1)Master Thread:核心后臺線程,負責調度其他線程,還負責將緩沖池中的數據異步刷新到磁盤中,保持數據的一致性,還包括臟頁的刷新、合并插入緩存、undo頁的回收。
2)I0 Thread:在lnnoDB存儲引擎中大量使用了AIO來處理IO請求,這樣可以極大地提高數據庫的性能,而IOThread主要負責這些lO請求的回調,線程類型如下所示:
線程類型 | 默認個數 | 職責 |
---|---|---|
Read thread | 4 | 負責讀操作 |
Write thread | 4 | 負責寫操作 |
Log thread | 1 | 負責將日志緩沖區刷新到磁盤 |
Insert buffer thread | 1 | 負責將寫緩沖區內容刷新到磁盤 |
3)Purge Thread:主要用于回收事務已經提交了的undolog,在事務提交之后,undolog可能不用了,就用它來回收。
4)Page Cleaner Thread:協助MasterThread刷新臟頁到磁盤的線程,它可以減輕Master Thread的工作壓力,減少阻塞。
事務原理
事務:事務是一組操作的集合,它是一個不可分割的工作單位,事務會把所有的操作作為一個整體一起向系統提交或撤銷操作請求,即這些操作要么同時成功,要么同時失敗,其特性如下所示:
1)原子性(Atomicity):事務是不可分割的最小操作單元,要么全部成功,要么全部失敗。
2)一致性(Consistency):事務完成時,必須使所有的數據都保持一致狀態。
3)隔離性(Isolation):數據庫系統提供的隔離機制,保證事務在不受外部并發操作影響的獨立環境下運行。
4)持久性(Durability):事務一旦提交或回滾,它對數據庫中的數據的改變就是永久的。
這些特性涉及的原理主要分為以下兩者情況:
1)redo log:重做日志,記錄的是事務提交時數據頁的物理修改是用來實現事務的持久性,該日志文件由兩部分組成:重做日志緩沖(redologbuffer)以及重做日志文件(redologfile),前者是在內存中,后者在磁盤中。
當事務提交之后會把所有修改信息都存到該日志文件中,用于在刷新臟頁到磁盤,發生錯誤時,進行數據恢復使用,如下所示:
2)undo log:回滾日志,用于記錄數據被修改前的信息,作用包含兩個:提供回滾和 MVCC(多版本并發控制)。
undo log和redo log記錄物理日志不一樣,它是邏輯日志。可以認為當delete一條記錄時,undo log中會記錄一條對應的insert記錄,反之亦然,當update一條記錄時,它記錄一條對應相反的update記錄。
當執行rollback時,就可以從undolog中的邏輯記錄讀取到相應的內容并進行回滾,如下所示:
undo log銷毀:undo log在事務執行時產生,事務提交時,并不會立即刪除undo log,因為這些日志可能還用于MVCC。
undo log存儲:undo log采用段的方式進行管理和記錄,存放在前面介紹的rollback segment回滾段中,內部包含1024個undo log segment。
MVCC
MVCC:通過在數據庫中保存多個數據版本,允許讀操作不加鎖,同時寫操作不會阻塞讀取從而提高并發性能,MVCC依賴于事務的快照隔離(Snapshot Isolation)級別,確保每個事務看到的是一致的視圖,對于讀取數據主要分為以下兩種:
1)當前讀:讀取的是記錄的最新版本,讀取時還要保證其他并發事務不能修改當前記錄,會對讀取的記錄進行加鎖,對于我們日常的操作,如:select...lock in share mode(共享鎖),select...for update、update、insert、delete(排他鎖)都是一種當前讀。
2)快照讀:簡單的select(不加鎖)就是快照讀,快照讀讀取的是記錄數據的可見版本,有可能是歷史數據,不加鎖,是非阻塞讀。
Read Committed:每次select都生成一個快照讀。
Repeatable Read:開啟事務后第一個select語句才是快照讀的地方。
Serializable:快照讀會退化為當前讀。
其實現原理如下圖所示:
MVCC使得讀寫操作沒有沖突,快照讀為MySQL實現MVCc提供了一個非阻塞讀功能,MVCC的具體實現還需要依賴于數據庫記錄中的三個隱式字段、undo log日志、readView,如下:
隱式字段:當我們創建一張表的時候,默認記錄中會有三個隱藏的字段,如下所示:
隱藏字段 | 含義 |
---|---|
DB_TRX_ID | 最近修改事務ID,記錄插入這條記錄或最后一次修改該記錄的事務ID。 |
DB_ROLL_PTR | 回滾指針,指向這條記錄的上一個版本,用于配合undolog,指向上一個版本。 |
DB_ROW_ID | 隱藏主鍵,如果表結構沒有指定主鍵,將會生成該隱藏字段。 |
undo log日志:回滾日志,在insert、update、delete的時候產生的便于數據回滾的日志:
當insert的時候,產生的undolog日志只在回滾時需要,在事務提交后,可被立即刪除
當update、delete的時候,產生的undolog日志不僅在回滾時需要,在快照讀時也需要,不會立即被刪除。
不同事務或相同事務對同一條記錄進行修改,會導致該記錄的undo log生成一條記錄版本鏈表,鏈表的頭部是最新的舊記錄,鏈表尾部是最早的舊記錄,如下所示:
readView:readView(讀視圖)是快照讀SQL執行時MVCC提取數據的依據,記錄并維護系統當前活躍的事務(未提交的)id,readView中包含了四個核心字段如下所示:
字段 | 含義 |
---|---|
m_ids | 當前活躍的事務ID集合 |
min_trx_id | 最小活躍事務ID |
max_trx_id | 預分配事務ID,當前最大事務ID+1(因為事務ID是自增的) |
creator_trx_id | ReadView創建者的事務ID |
版本鏈數據的訪問規則如下所示:
不同的隔離級別,生成的readView的時機不同,如下所示:
1)read committed:在事務中每一次執行快照讀時生成的readView,如下所示:
比如當前我們訪問這條記錄的事務id小于我們的最小活動事務的id,那么就說明當前的這條記錄已經提交了,此時就說明這一次快照讀就是我們當前訪問的這條記錄,如下是DB_TRX_ID為2就是我們要訪問的記錄:
2)repeatable read:僅在事務第一次執行快照讀時生成readView,后續復用該readView