InnoDB運行時內存體系架構

InnoDB引擎在運行期間,實際上就是一個用戶進程來做爲客戶與磁盤之間交互的一個通道。而在內存上,InnoDB引擎實際上分爲兩大塊區域:後臺線程內存池算法

後臺線程

InnoDB是多線程模型,因此在運行過程當中有多個不一樣的後臺線程,分別執行不一樣的任務。sql

Master Thread

Master Thread是全部後臺線程中所核心的一個,主要負責髒頁的刷新,插入緩衝的合併,Undo log的回收(1.1版本以前)等核心任務緩存

IO Thread

在InnoDB中大量使用了AIO來提高IO效率,而其中的IO線程能夠分爲四種:write,read,insert,log IO Thread,分別負責對這四種IO操做進行發起和回調。在1.0及其以前的版本中,這四種線程各有一種。而在以後的版本,write金和read線程數就增長到各位4個多線程

Purge Thread

InnoDB實現了事務機制,而事務的一些基礎功能如回滾,是經過每一個事務的Undo log來實現的,也就是說,當事務提交以後,其內部造成的版本鏈就沒有存在的意義了,因此此時Purge Thread就負責回收這些Undo log(其實在1.1版本以前,這個操做是由Master Thread去作的,後來就加入Purge Thread用來減輕Master Thread的負擔)性能

內存

緩衝池

緩衝池其實包含了挺多東西的:操作系統

主要其實就是三個:數據頁,插入緩衝和自適應哈希索引 其實在1.0以後的版本之中,InnoDB在運行時期有多個緩衝池實例,數據頁哈希到不一樣緩衝池上進行存放。

數據頁

通常來講爲了協調CPU和磁盤處理速度上數量級的差距,操做系統都會有設置緩存的作法, 把屢次IO合併變成一次IO,從而提高效率。InnoDB也不例外。InnoDB不會對每一次修改操做都進行一次磁盤IO,那未免也太慢了。因此在內存上,InnoDB維護了一個數據頁區域,對該數據頁的修改,都會先在內存上找到該數據頁(若是內存中沒有怎麼辦?稍後再說),這個數據頁修改以後,就變成了一個髒頁,等待合適的時機,再會把這個髒頁刷新回磁盤中。所以,爲了防止存在內存的這部分數據由於宕機而丟失,從而違背事務的持久性,InnoDB還會有一個Redo log機制來保證持久性。 那問題來了,既然是做爲一個緩存,那大小確定有限制,總不能把全部的數據頁都讀到內存中來吧,因此緩存淘汰機制確定是會有的,最典型的不就是LRU算法(最近最久未使用)麼?沒錯,InnoDB用的也是LRU,但不一樣的是,它用的並非樸素的LRU算法:當有一個最新的頁插入時,它並非插入到隊列頭,而是默認會插入到隊列5/8的的位置,爲啥要這麼設計呢?其實是爲了讓真正的熱門數據頁不要爲刷出去。 考慮這樣的狀況:內存中存有一堆熱門數據頁,此時來了一個不多執行的大範圍查詢,涉及不少數據頁,若是使用樸素的LRU,那麼原來真正的熱門數據頁就全被刷走了,從而對性能產生影響。線程

Redo log Buffer

Redo log的存在就是爲了保證事務的持久性,它記錄的內容並非像bin log那樣的sql語句,它記錄的其實是數據頁的物理偏移量,一條修改操做的執行,實際上得先在redo log buffer中記錄,而後再去到內存中的數據頁進行更新,redo log buffer會在合適的時機刷新到磁盤中的redo log file裏面,後續若是須要重啓恢復數據,都是基於redo log file去作的,刷新的時機有幾個:設計

  • 當事務提交時,會將redo log buffer的修改刷新到redo log file中
  • Master Thread會以每秒一次的頻率進行數據刷新
  • 當redo log buffer大小佔用達到閾值(默認是8M)的一半時,就進行一次刷新 那這樣的話,又衍生了兩個問題:文件大小髒頁刷新到一半宕機,這兩個分別使用checkpoint和**雙寫(double write)**來解決

Check Point

由於redo log file是以一種增量追加的方式寫入,因此隨着進程運行,file會一直膨脹,這涉及到一個空間問題;而且進行重作的時候,須要對整個file進行重作,這又是一個時間問題。然而事實上,重作日誌裏面記錄的信息,有不少是已經落盤了,對於這些信息是不必進行重作的,因此就有了CheckPoint這個機制 Check Point能夠理解爲在重作日誌中的一個指針,當一個髒頁刷新回磁盤的時候,Check Point就向前移動,意義就是:Check Point以前的修改已經落盤了,重作的時候能夠忽視,而且能夠被覆蓋;Check Point以後的數據還沒落盤,恢復的時候須要重作,且不能被覆蓋。 這樣就解決了文件大小和恢復時間的問題指針

雙寫(double write)

因爲redo log file記錄的只是數據頁的一個物理偏移量,因此若是原來的數據頁發生了變換,那它是沒法正常重作的。好比說,Mysql的一個數據頁默認是16Kb大,在進行髒頁刷新的時候,若是刷了前面的8Kb,而後宕機了,這樣磁盤中的數據頁已經被修改了,再根據重作日誌進行恢復是沒有意義的,因此必須有一個機制,在髒頁徹底刷新以前,必須維護着一個原始的副本,這樣即便髒頁刷新到一半失敗了,還能夠將數據頁恢復成副本,而後再進行重作日誌

此時就是double write大顯身手的時候了。double wirite機制相關的有兩個部分:存在於內存上的double write buffer磁盤上的共享表空間

髒頁在進行刷新以前,必須把對應的髒頁複製到doule write buffer中

第一次寫

把double write buffer中的髒頁寫到磁盤的共享表空間上,做爲該數據頁的一個備份

第二次寫

把double write buffer中的髒頁刷新到磁盤表數據文件中

若第二次寫發生了錯誤,那直接從共享表空間上進行恢復,而後進行redo,就能夠完成數據的備份

插入緩衝

前面提到了,對於一些修改操做好比插入,InnoDB會將插入操做先同步到內存中的數據頁使之成爲髒頁,那問題來了,若是對應的數據頁沒有在內存中呢?要從磁盤中拿出來嗎?那也太麻煩了。因此Insert Buffer就出現了,當對應的數據頁沒有在內存中的話,那這條插入記錄會先記錄在Insert Buffer中,等到合適的時機再會合併到數據頁上,那這個時機主要有兩個:

  • 對應的數據頁從磁盤中被讀到內存中了,此時剛好能夠寫入
  • Master Thread會以每10秒一次的機率進行一次插入緩衝的合併 固然了,插入緩衝也是在任什麼時候候都會使用的,有一些限制:
  • 對應的數據存在輔助索引(也就是非主鍵索引)
  • 對應的索引不是惟一索引,由於惟一索引每次插入以前都得判斷一下是否破壞了惟一性,這種數據是沒辦法緩衝的

那說了這麼多,Insert Buffer究竟是個啥?其實它就是一個B+樹的結構,在以前的版本中,每一個表都有這麼一個Insert Buffer B+樹,而在以後,變成了全局一個,按照表的全局惟一Id進行區分

自適應哈希索引

當以相同的模式(指的是相同的條件條件)對同一個頁進行連續訪問後,InnoDB會認爲這些頁是熱門頁,爲這些頁創建哈希索引,這種哈希索引就叫作自適應哈希索引。自適應哈希索引的創建有兩個條件,知足其中一個便可:

  • 以相同的模式對該數據頁查詢了100次
  • 以相同的模式對該數據頁查詢了N次,N爲該數據頁數據行總數的1/16
相關文章
相關標籤/搜索