近期企業內部分享了這篇架構,粘出來分享給你們.
參考資料:https://dev.mysql.com/doc/ref...html
InnoDB存儲引擎是平衡了高可靠和高性能的存儲引擎,它也是Mysql8.0中默認的存儲引擎.mysql
從架構圖中可見,InnoDB引擎可劃分爲兩大部分:內存結構和硬盤結構.算法
內存部分(Memory)又包含Buffer pool, change buffer, Log Buffer 等,經過操做系統緩存與硬盤結構進行交互.sql
硬盤部分包含若干個表空間,如系統表空間,單文件表空間,通常表空間,臨時表空間,undo表空間和redo log. 數據庫
1.Buffer Pool(緩衝池)緩存
緩衝池是主內存的一部分,它用於在咱們查詢訪問任何表和索引數據時進行緩存.據官方文檔介紹的統計,高達80%的查詢來會由緩存池命中.session
在介紹緩存池的官方文檔中,咱們能夠見到一個分明另外的lru算法.數據結構
首先,緩衝池能夠理解爲一個塞滿page的鏈表,注意,其中單位是頁(page, innodb引擎的存儲單位自上到下爲表空間 table spaces,片斷 segemant, 分區 extend, 頁 page, 行row,列 column),也就是說,緩存是以頁爲單位的,io操做將硬盤中的數據讀入緩衝的單位也是頁.架構
其次,緩衝隊列其實有兩個sublist,最新的5/8爲new sublist,較老的3/8爲old sublist.new sublist 的tail與old sublist 的head相鄰,這個相鄰點被官方文檔稱之爲midpoint(中點).較新訪問的數據放置於new sublist,反之放置於old sublist.併發
第三,當咱們從硬盤中讀取新的頁併入池時,它是放置於中點的.固然,這個"讀"包含咱們用戶的初始化操做(user-initiated, 包含sql查詢),也包含引擎爲優化而作的預讀操做(read-ahead).
第四,有關移動操做,對於old sublist的頁成員進行讀取操做時,分兩種狀況,若爲用戶初始化操做形成,則會當即將其移動到new list頭,若爲預讀操做形成,則可能不會移動.
第五,每一個頁的位置至關於一個虛擬的"年齡",每當buffer pool有一個頁被讀取,都會形成其餘頁的年齡加1(實際效果即向後移位).同時,old sublist的成員也會因一些頁的初次讀入緩存而老化,經過這兩個機制的共同做用,new sublist的元素不停地移入old sublist,old sublist中的數據可能會慢慢過時或獲得新生.
2.change buffer(變動緩衝)
輔助索引和聚簇索引有什麼區別?除了耳熟能詳的前者須要二次io之外,變動緩衝也是二者在查詢上的一個不一樣點.
change buffer的做用點在於,對於一些數據的二級索引進行更新操做(insert update delete都在此更新範圍),而相應的輔助索引頁還並未加載進緩衝池時,將相應的對輔助索引的更新緩存到change buffer,直到當後續對相應的頁進行了讀操做時,將這個結果合併進buffer pool.
這樣實現的理由也很簡單,咱們知道輔助索引(二級索引)不像聚簇索引同樣惟一,它的插入操做相對爲隨機寫.對二級索引的刪除和更新操做也可能會影響到在索引樹中根本不相鄰的數據頁,所以將它暫時緩衝起來,在後續使用到變動索引的讀操做發生時統一合併入buffer pool,再由buffer pool返回讀取的結果,能夠減小大量的隨機寫操做.
同時,當mysql進行週期淨化(purge)操做時,或進行慢關閉時,能夠將這些change buffer批量地寫入到硬盤,後者會寫入到系統表空間的change buffer,從而減小了多個單次io的開銷.
特殊狀況,當二級索引包含降序索引列,或主鍵包含降序索引列時,change buffer將不可用.change buffer的批量落盤操做會形成mysql瞬間的io開銷增大.
3.adaptive hash index (自適應哈希索引)
自適應哈希索引做用於緩衝池中的數據,當有效地平衡了工做負載和緩衝池內存時,咱們使用自適應哈希索引能夠令mysql在不損壞任何事務性和可靠性的前提下逼近內存數據庫的性能.
它是自適應的(意味着能夠由innodb自行觀測查詢頻率,並估算優化的結果和構建哈希索引的開銷比),它的構建是使用索引key的左前綴實現的,主要針對頻繁訪問的數據.
有兩種特殊地場景,高併發地訪問自適應哈希索引(如併發地進行join)可能會是一個重要的競態來源,當咱們大量使用通配符%或like操做時,自適應哈希索引將毫無益處,此時能夠經過重啓服務並加上--skip-innodb-adaptive-hash-index關閉之.
4 log buffer
log buffer是一塊內存區域,用於保存將要刷盤的日誌操做,它有兩個落盤的機制,一是週期性地落盤,二是事務提交後落盤,所以當咱們使用大事務時,適當調大它的值,能夠減小無謂的io.
5.system tablespace (系統表空間)
系統表空間是在硬盤中存放double write buffer和change buffer的表空間,同時,若一個表建立於系統表空間(而不是單文件表空間或通常表空間)時,它可存放這些表的表數據和索引數據.
6.File-Per-Table Tablespaces (單文件表空間)
據官方文檔介紹,單文件表空間相對於傳統的全部表數據存放於系統表空間的優點在於更加靈活,它使得每一個表能夠有本身的表空間文件.
7.General Tablespace (通常表空間)
通常表空間是共享的,由create tablespace語法建立的表空間.它與系統表空間相似,存放可供多表使用的共享數據.它具有先天的使用優點,如在完整生命週期內將表空間的數據加載至內存,則可減小相似單文件表空間的頻繁io開銷.
8.undo tablespaces
undo log包含於undo段,undo段包含於回滾段,而undo表空間就是存放了可用於回滾事務的undo log.
9.temporary tablespaces(臨時表空間)
臨時表空間包含兩個級別:會話級別的臨時表空間和全局的臨時表空間.
會話級臨時表空間每次使用時會從一個臨時表空間池中取出並和session進行attach,默認狀況下,server啓動時會建立一個擁有10個臨時表空間的池供使用,當會話釋放時,它持有的臨時表空間會被清空並還池.
會話級臨時表空間默認大小爲5個page大小,而且以.ibt結尾.
全局級臨時表空間用於存放對於用戶建立的臨時表的變動回滾段.在server關閉或放棄了全局級臨時表空間的初始化操做時銷燬,在server每次啓動時重建.
10.double write buffer(雙寫緩衝)
雙寫緩衝存放於系統表空間,是innodb從內存模塊的buffer pool取出的頁刷新到磁盤的目的地,這也說明對於innodb的寫操做實際上是二次寫,但這並不影響宏觀的性能,反而增大了吞吐量.double write buffer中已寫入的數據將在後續刷入它真正的目的地,若在此過程當中出現宕機等事故,mysql server將能夠在double write buffer中找到一個準確的copy.
數據頁刷入double write buffer的過程使用了操做系統的fsync()函數,將較大的數據量統一處理,順序寫入.這一特性能夠手動關閉,或者在支持原子寫的Fusion-io設備上運行時,將自動禁用double write buffer.
11 redo log(重放日誌)
重放日誌也是一個用於恢復數據的存放於硬盤的數據結構,但它做用於那些未完成的事務.在一次crash保存的未完成的對某些數據文件的更改能夠在後續服務啓動後的初始化過程當中基於redo log自動重放.
重放日誌在物理上體現爲兩個文件:ib_logfile0,ib_logfile1,mysql中對這兩個文件的使用是"環形方式",這意味着能夠交替重用.redo log中的日誌標識是一個按LSN(日誌順序號)永遠遞增的數.
爲了節省性能,重放日誌也支持按組提交落盤(批量).日誌自己支持壓縮.
12 undo log(回滾日誌)
undo log是單次讀寫事務的回滾日誌,它包含了對於一個事務來講必要的針對一個聚簇索引記錄的最近修改的回滾信息,若是有一個事務在一致讀場景下須要讀到原版本數據,它能夠從它事務的undo log中找到相應的數據.
mysql innodb引擎的隔離級別實現與undo log有重大關聯,當隔離級別爲讀已提交時,事務對數據的讀取永遠是取最新提交,並不須要undo log,但當級別爲可重複讀時,每次讀取須要利用undo log進行.
innodb是一個支持多版本併發的存儲引擎,前面已經提過,innodb利用表空間的回滾段(包含了undo log)來保存了爲支持事務的併發和回滾而必需的,被修改數據行的老版本信息,它使用該信息進行回滾以及一致性讀等操做.
關於mvcc的內部實現,innodb爲每一行數據多維護了隱式的三個field,分別是6字節長的DB_TRX_ID,7字節長的DB_ROLL_PTR,6字節的DB_ROW_ID.
DB_TRX_ID:它表示最近對一個行的數據的插入或更新的事務id(刪除在此理解爲一種更新操做,只是行中有一個位表示標記爲刪除).
DB_ROLL_PTR:即滾動指針,它指向undo段的一個undo log.當發生該行的更新時,undo log中包含了重建歷史數據的所有信息.
DB_ROW_ID:行id,它是一個在新行插入時單調遞增的值,當innodb自動生成聚簇索引時,索引將包含行id,不然行id不出如今任何索引中.
存放於undo段的undo log區分爲insert和update兩種(後者包含delete),前者能夠在事務提交以後直接捨棄,後者因一致性讀的緣由(這些讀須要訪問歷史版本的數據),只能在全部快照均沒有事務在使用時才能捨棄.
所以,須要及時地提交事務,尤爲那些包含一致讀的事務,不然undo log會愈來愈大,最終充滿表空間.
涉及mvcc,被刪除的數據行不會當即物理刪除,直到有關的undo log被刪除,這一操做稱爲purge,而且它會按照實際刪除的順序進行.
對於mvcc,innodb在聚簇索引和二級索引實現上稍有不一樣,聚簇索引能覆蓋的記錄會就地更新,而指向早期版本記錄的undo log指向的一些隱藏信息條目能夠進行重建,但對於二級索引,它並不包含隱藏系統列,也不進行原地更新.
當二級索引被更新時,老的二級索引記錄會被標記刪除,新的二級索引會插入,標記刪除的老二級索引記錄會最終被清理,若是已經開啓了一個針對它的讀事務,且發生了寫操做,則從undo log中查找相應的版本記錄.
當二級索引被刪除時,覆蓋索引將不可用,即不從索引樹中查找,而是從聚簇索引記錄中查找.
當使用索引條件下推優化時,且只有部分where查詢條件可以使用到索引,mysql server仍舊會將全條件下推到存儲引擎,而存儲引擎會即便相應的索引取值,若是沒有取到值,則省去了後續的使用聚簇索引再次查找的過程.若是有記錄(甚至多是被標記刪除但能匹配的記錄,還未被purge,多是由於mvcc),則無可避免後續的,還須要進行一次的利用聚簇索引查找的過程.