MySQL InnoDB 存儲引擎探祕

在MySQL中InnoDB屬於存儲引擎層,並以插件的形式集成在數據庫中。從MySQL5.5.8開始,InnoDB成爲其默認的存儲引擎。InnoDB存儲引擎支持事務、其設計目標主要是面向OLTP的應用,主要特色有:支持事務、行鎖設計支持高併發、外鍵支持、自動崩潰恢復、聚簇索引的方式組織表結構等。mysql

體系架構

InnoDB存儲引擎是由內存池、後臺線程、磁盤存儲三大部分組成。sql

線程

InnoDB 使用的是多線程模型, 其後臺有多個不一樣的線程負責處理不一樣的任務數據庫

Master Thread

Master Thread是最核心的一個後臺線程,主要負責將緩衝池中的數據異步刷新到磁盤,保證數據的一致性。包括髒頁刷新、合併插入緩衝、UNDO頁的回收等。緩存

IO Thread

在 InnoDB 存儲引擎中大量使用了異步IO(Async IO)來處理寫IO請求, IO Thread的工做主要是負責這些 IO 請求的回調。markdown

Purge Thread

事務提交後,其所使用的undo log可能再也不須要,所以須要Purge Thread來回收已經分配並使用的UNDO頁。InnoDB支持多個Purge Thread, 這樣作能夠加快UNDO頁的回收,提升CPU的使用率以及提高存儲引擎的性能。數據結構

Page Cleaner Thread

Page Cleaner Thread的做用是取代Master Thread中髒頁刷新的操做,其目的是減輕原Master Thread的工做及對於用戶查詢線程的阻塞,進一步提升InnoDB存儲引擎的性能。多線程

內存

InnoDB 存儲引擎內存的結構架構

緩衝池

InnoDB存儲引擎是基於磁盤存儲的,並將其中的記錄按照頁的方式進行管理。可是因爲CPU速度和磁盤速度之間的鴻溝,基於磁盤的數據庫系統一般使用緩衝池記錄來提升數據庫的的總體性能。併發

緩衝池其實就是經過內存的速度來彌補磁盤速度較慢對數據庫性能的影響。在數據庫進行讀取操做時,首先將磁盤中的頁放入緩衝池中,下次再讀取相同頁時,首先從緩衝池中獲取該頁數據,起到高速緩存的做用。異步

數據的修改操做,則首先修改在緩衝池中的頁數據,而後使用一種稱爲Checkpoint的機制刷新到磁盤上。

緩衝池的大小直接影響數據庫的總體性能,對於InnoDB存儲引擎而言,緩衝池配置經過參數 innodb_buffer_pool_size 來設置。使用 SHOW VARIABLES LIKE 'innodb_buffer_pool_size' 命令可查看緩衝池配置:

mysql> SHOW VARIABLES LIKE 'innodb_buffer_pool_size' \G *************************** 1. row *************************** Variable_name: innodb_buffer_pool_size  Value: 134217728 1 row in set (0.01 sec) 

緩衝池中緩存的數據頁類型有: 索引頁、undo頁、插入緩衝、自適應哈希索引、InnoDB鎖信息、數據字典信息等,索引頁和數據頁佔緩衝池很大的一部分。

重作日誌緩衝

緩衝池中的頁數據比磁盤要新時,須要將新數據刷新到磁盤中。InnoDB採用Write Ahead Log策略來刷新數據,即當事務提交時,先寫入重作日誌緩衝,重作日誌緩衝會按必定頻率刷新到重置日誌文件中,而後髒頁會根據checkpoint機制刷新到磁盤。

重作日誌緩衝不須要設置很大,一般狀況下8M能知足大部分的應用場景。重作日誌支持一下三種狀況觸發刷新:

  • Master Thread每一秒將重作日誌緩衝刷新到重作日誌文件
  • 每次事務提交時將重作日誌緩衝刷新到重作日誌文件
  • 當重作日誌緩衝池剩餘空間小於1/2時,重作日誌緩衝刷新到重作日誌文件

額外內存池

在InnoDB存儲引擎中,對內存的管理是經過一種稱爲內存堆的方式進行的。在對一些數據結構自己的內存進行分配時,須要從額外的內存池中進行申請,當該區域的內存不夠時,會從緩衝池中進行申請。

InnoDB支持的鎖有:

  • 共享鎖和排它鎖
  • 意向鎖
  • 記錄鎖
  • 間隙鎖
  • 自增鎖

共享鎖和排他鎖

InnoDB引擎實現了兩種標準的行級鎖,共享鎖(shared (S) locks) 和排他鎖 (exclusive (X) locks)。共享鎖容許一個佔有鎖的事務去讀取一行數據,排它鎖則容許事務對某一行記錄進行寫操做。

若是一個事務持有了一個共享鎖,其餘事務仍然能夠獲取這行記錄的共享鎖,但不能獲取到這行記錄的排它鎖。當一個事務獲取到了某一行的排它鎖,則其餘事務將沒法再獲取這行記錄的共享鎖和排它鎖。

意向鎖

在InnoDB中,意向鎖是一種表級鎖,分爲共享鎖和排他鎖:

  • 意向共享鎖:將要去獲取某一行的共享鎖
  • 意向排它鎖:將要去獲取某一行的排它鎖

事務在獲取共享/排它鎖以前必須先獲取意向共享/排它鎖,意向鎖不會阻塞其餘任何對錶的操做,他只是告訴其餘事務他將要去獲取某一行的共享鎖或者排他鎖。

記錄鎖

記錄是是做用在索引上的一種鎖,他鎖住的是某一條記錄的索引而非記錄自己,若是當前表沒有索引那麼InnoDB將會爲其建立一個隱藏的彙集索引,而Record Locks將會鎖住這個隱藏的彙集索引。

間隙鎖

間隙鎖和記錄鎖同樣也是做用在索引上,不一樣的是記錄鎖只做用於一條索引記錄而間隙鎖能夠鎖住一個範圍內的索引。間隙鎖在InnoDB的惟一做用就是防止其餘事務的插入操做,以此防止幻讀的發生。

自增鎖

自增鎖是一種特殊的表級鎖,他只做用在包含自增列的插入操做時。當一個事務正在插入一條數據時,其餘的任何事務都必須等待整個事務完成插入操做,在取獲取鎖來執行插入操做。

事務

ACID

事務是數據庫做爲OLTP最爲重要的特性,提及事務不得不提起ACID四個基本特性:

  • 原子性(Atomicity) :事務最小工做單元,要麼全成功,要麼全失敗
  • 一致性(Consistency): 事務開始和結束後,數據庫的完整性不會被破壞
  • 隔離性(Isolation) :不一樣事務之間互不影響,四種隔離級別爲RU(讀未提交)、RC(讀已提交)、RR(可重複讀)、SERIALIZABLE (串行化)
  • 持久性(Durability) :事務提交後,對數據的修改是永久性的,即便系統故障也不會丟失

InnoDB的原子性、持久性和一致性主要是經過Redo Log、Undo Log和Force Log at Commit機制機制來完成的。Redo Log用於在崩潰時恢復數據,Undo Log用於對事務的影響進行撤銷,也能夠用於多版本控制。而Force Log at Commit機制保證事務提交後Redo Log日誌都已經持久化。隔離性則是由鎖和MVCC來保證的。

隔離級別

在MySQL中,事務有4種隔離級別,分別是:

  • Read Uncommitted 未提交讀
  • Read Committed 已提交讀
  • Repeatable Read 可重複讀
  • Serializable 可串行化

在理解四種隔離級別以前,咱們須要先了解另外三個名詞:

  • 髒讀

    a事務會讀取到b事務還未提交的數據,可是b事務因爲某種緣由進行回滾操做,這樣,a事務讀取的數據是不可用的,進而會形成一些異常結果。

  • 不可重複讀

    a事務週期內對某一數據屢次查詢,同時這些數據在b事務中進行了update或delete操做。那麼a事務每次查詢出來的結果可能都不同。

  • 幻讀

    幻讀的結果其實和不可重複讀是同樣的表現,差別就在於不可重複讀主要是針對其餘事務進行了編輯(update)和刪除(delete)操做。而幻讀主要是針對插入(insert)操做。也就是在一個事務生命週期內,會查詢到另一個事務新插入的數據。

Read uncommitted 未提交讀

未提交讀,這種狀況下,一個事務a能夠看到另外一個事務b未提交的數據,若是此時事務b發生回滾,那麼事務a拿到的就是髒數據,這也就是髒讀的含義。

此隔離級別在MySQL InnoDB通常不推薦使用。

Read Committed 已提交讀

已提交讀,一個事務從開始直到提交以前,所作的任何修改對其餘事務都是不可見的。解決了髒讀問題,可是存在幻讀現象。

Repeatable Read 可重複讀

可重複讀,該級別保證在同一事務中屢次讀取一樣記錄的結果是一致的,在InnoDB存儲引擎中同時解決了幻讀和不可重複讀問題。

InnoDB引擎經過使用Next-Key Lock解決了幻讀的問題。Next-Key Lock是行鎖和間隙鎖的組合,當InnoDB掃描索引記錄的時候,會首先對索引記錄加上行鎖(Record Lock),再對索引記錄兩邊的間隙加上間隙鎖(Gap Lock)。加上間隙鎖以後,其餘事務就不能在這個間隙修改或者插入記錄。

Serializable 可串行化

Serializable 是最高的隔離級別,它經過強制事務串行執行,避免了幻讀的問題,可是 Serializable 會在讀取的每一行數據上都加鎖,因此可能致使大量的超時和鎖爭用的問題,所以併發度急劇降低,在MySQL InnoDB一樣不被建議使用。

開啓事務

  • BEGIN、BEGIN WORK、START TRANSACTION

    執行BEGIN命令不會真正在引擎層開啓新事務,僅僅是爲當前線程設定標記,表示爲顯式開啓的事務。

  • START TRANSACTION READ ONLY

    開啓只讀事務,當MySQL Server接收到任何數據更改的SQL時,都會直接拒絕修改並返回錯誤,此錯我不會進入引擎層。

  • START TRANSACTION READ WRITE

    容許super用戶在當前線程只讀狀態爲true的狀況下啓動讀寫事務。

  • START TRANSACTION WITH CONSISTENT SNAPSHOT

    開啓事務會進入引擎層,並開啓一個readview。只有在RR隔離級別下,這種操做纔有效,不然會報錯。

Undo log

在數據進行修改時會記錄相應的undo日誌,若是事務失敗或者回滾,能夠藉助記錄的undo日誌進行回滾。Undo log是邏輯日誌,記錄更改前的數據鏡像。在修改時若是同時須要讀取當前數據的時候,它能夠根據版本信息分析出該行記錄之前版本的數據。另外Undo log也會產生重作日誌,由於Undo log也要進行持久化保護。

事務提交

  1. 使用全局事務ID產生器生成事務NO,將當前鏈接的事務指針(trx_t)添加到全局提交事務鏈表(trx_serial_list)中
  2. 標記undo,若是這個事務只使用了一個UndoPage且使用量小於3/4個Page,則把這個Page標記爲TRX_UNDO_CACHED,若是不知足且是insert undo則標記爲TRX_UNDO_TO_FREE,不然undo爲update undo則標記爲TRX_UNDO_TO_PURGE。標記爲TRX_UNDO_CACHED的undo會被引擎回收。
  3. update undo放入所在undo segmenthistory list,並遞增rseg_history_len(全局)。同時更新Page上的TRX_UNDO_TRX_NO, 若是刪除了數據,則重置delete_mark
  4. undate undoupdate_undo_list中刪除,若是被標記爲TRX_UNDO_CACHED,則加入到update_undo_cached隊列中
  5. mtr_commit(日誌undo/redo寫入公共緩衝區),至此,在文件層次事務提交。這個時候即便crash,重啓後依然能保證事務是被提交的。接下來要作的是內存數據狀態的更新(trx_commit_in_memory)
  6. 只讀事務只須要把readview從全局readview鏈表中移除,而後重置trx_t結構體裏面的信息便可。讀寫事務首先須要是設置事務狀態爲TRX_STATE_COMMITTED_IN_MEMORY,釋放全部行鎖而且將trx_trw_trx_list中移除,readview從全局readview鏈表中移除。若是有insert undo則在這裏移除,若是有update undo則喚醒Purge線程進行垃圾清理,最後重置trx_t裏的信息,便於下一個事務使用

回滾

  • 若是是隻讀事務,則直接返回
  • 判斷當前是回滾整個事務仍是部分事務,若是是部分事務,則記錄下須要保留多少個Undo log,多餘的全進行回滾
  • update undoinsert undo中找出最後一條undo,從這條undo開始回滾
  • 若是是update undo則將標記爲刪除的記錄清理標記,更新過的數據回滾到最老的版本。若是是insert undo則直接刪除彙集索引和二級索引
  • 若是全部undo都已經被回滾或者回滾到了指定的undo則中止,把Undo log刪除

索引

InnoDB引擎使用B+樹做爲索引結構,主鍵索引的葉子節點data域保存了完整的字段數據,非主鍵索引的葉子節點保存了指向主鍵的值數據。

上圖是 InnoDB 主索引(同時也是數據文件)的示意圖,能夠看到葉節點包含了完整的數據記錄,這種索引叫作彙集索引。由於 InnoDB 的數據文件自己要按主鍵彙集,因此 InnoDB 要求表必須有主鍵,若是沒有顯式指定,則 MySQL 系統會自動選擇一個能夠惟一標識數據記錄的列做爲主鍵,若是不存在這種列,則 MySQL 自動爲 InnoDB 表生成一個隱含字段做爲主鍵,這個字段長度爲6個字節,類型爲長整形。

InnoDB 的輔助索引 data 域存儲相應記錄主鍵的值而不是地址。換句話說,InnoDB 的全部輔助索引都引用主鍵做爲 data 域。彙集索引這種實現方式使得按主鍵的搜索十分高效,可是輔助索引搜索須要檢索兩遍索引:首先檢索輔助索引得到主鍵,而後用主鍵到主索引中檢索得到記錄。

結尾

對於MySQL InnoDB的諸多特性,本文只介紹了很小的一部分,感興趣的同窗可閱讀 《MySQL技術內幕:InnoDB存儲引擎》瞭解更多相關知識。

相關文章
相關標籤/搜索