????從前面的文章,我們已經了解到了目前MySQL最常用的存儲引擎就是InnoDB,但是InnoDB具體是什么樣結構,是如何將數據保存的我們并沒有聊到,本期就簡單聊一聊InnoDB的底層結構與實現
????我們先看下MySQL官網給出的結構圖,如下:
從上圖我們可以很清楚的看到,InnoDB分為兩大塊:內存結構(in-Memory structures)、磁盤結構(in-Disk structures)
內存結構
????內存結構主要包括Buffer Pool、Change Buffer、Adaptive Hash Index和Log Buffer四大組件。接下來我們分別講下各個組件。
一、Buffer pool(緩沖池)
????緩沖池以頁為最小單位來存儲多行數據,是緩沖表、索引數據、高頻數據、緩沖池寫磁盤時候一般直接寫入系統表空間的雙寫緩沖區。對數據庫數據進行操作時都是先讀取到內存緩沖區進行操作后再寫入磁盤。
????通常來講Buffer Pool越大,能緩存的數據就更多,更多的操作都會發生在內存,從而達到提升效率的目的。
?1.1?頁
????是InnoDB中數據管理的最小單位。頁分為,數據頁(BTreeNode),Undo頁(undo Log page),系統頁(Systempage),事務數據頁(Transaction SystemPage)每個數數據頁的大小為16kb,每個page使用一個32位int值來表示。
????當我們查詢數據時,其是以頁為單位,將磁盤中的數據加載到緩沖池中的。同理,更新數據也是以頁為單位,將我們對數據的修改刷回磁盤。每頁的默認大小為16k,每頁中包含了若干行的數據,頁的結構如下圖所示。
不用太糾結每個區是干嘛的,我們只需要知道這樣設計的好處在哪兒。每一頁的數據,可以通過FileHeader中的上一頁和下一頁的數據,頁與頁之間可以形成雙向鏈表。因為在實際的物理存儲上,數據并不是連續存儲的。你可以把它理解成G1的Region在內存中的分布。
而一頁中所包含的行數據,行與行之間則形成了單向鏈表。我們存入的行數據最終會到User Records中,當然最初User Records并不占據任何存儲空間。隨著我們存入的數據越來越多,User Records會越來越大,Free Space的空間會越來越小,直到被占用完,就會申請新的數據頁。
User Records中的數據,是按照主鍵id來進行排序的,當我們按照主鍵來進行查找時,會沿著這個單向鏈表一直往后找。如下圖:
1.2、 Adaptive Hash Index::自適應哈希索引
????是配合Buffer Pool工作的一個功能。自適應哈希索引使得MySQL的性能更加接近于內存服務器。
? ??自適應哈希索引是根據索引Key的前綴來構建的,InnoDB 有自己的監控索引的機制,當其檢測到為當前某個索引頁建立哈希索引能夠提升效率時,就會創建對應的哈希索引。如果某張表數據量很少,其數據全部都在Buffer Pool中,那么此時自適應哈希索引就會變成我們所熟悉的指針這樣一個角色。
????? 如果要啟用自適應哈希索引,可以通過更改配置innodb_adaptive_hash_index來開啟。如果不想啟用,也可以在啟動的時候,通過命令行參數--skip-innodb-adaptive-hash-index來關閉。
????當然,創建、維護自適應哈希索引是會帶來一定的開銷的,但是比起其帶來的性能上的提升,這點開銷可以直接忽略不計。但是,是否要開啟自適應哈希索引還是需要看具體的業務情況的,例如當我們的業務特征是有大量的并發Join查詢,此時訪問自適應哈希索引被產生競爭。并且如果業務還使用了LIKE或者%等通配符,根本就不會用到哈希索引,那么此時自適應哈希索引反而變成了系統的負擔。
????所以,為了盡可能的減少并發情況下帶來的競爭,InnoDB對自適應哈希索引進行了分區,每個索引都被綁定到了一個特定的分區,而每個分區都由單獨的鎖進行保護。其實通俗點理解,就是降低了鎖的粒度。分區的數量我們可以通過配置innodb_adaptive_hash_index_parts來改變,其可配置的區間范圍為[8, 512]。
1.3、Change Buffer(變更緩沖區、有人又叫寫緩沖區)
????用來存儲二級索引變更的數據結構(緩沖池沒有該內容),可能由insert,update,delete等dml操作造成的。后面會由讀操作將頁面加載到緩沖池合并。緩沖池周期性地將更改的索引頁寫進磁盤。在內存中,變更緩沖區屬于緩沖池一部分,磁盤上,緩沖區屬于系統表空間一部分,當數據庫服務宕機時,索引變更存儲在內存變更緩沖區。
? ? 換句話說當我們更新了非聚簇索引(二級索引)的數據時,此時應該是直接將其在Buffer Pool中的對應數據更新了即可,但是不湊巧的是,當前二級索引不在 Buffer Pool 中,此時將其從磁盤拉取到 Buffer Pool 中的話,并不是最優的解,因為該二級索引可能之后根本就不會被用到,那么剛剛進行的昂貴的磁盤I/O操作就白費了。
????所以,我們需要這么一個地方,來暫存對這些二級索引所做的改動。當被緩存的二級索引頁被其他的請求加載到了Buffer Pool 中之后,就會將 Change Buffer 中緩存的數據合并到 Buffer Pool 中去。
當然,Change Buffer也不是沒有缺點。當 Change Buffer 中有很多的數據時,全部合并到Buffer Pool可能會花上幾個小時的時間,并且在合并的期間,磁盤的I/O操作會比較頻繁,從而導致部分的CPU資源被占用。
change buffer 占用BufferPool空間默認占25%,最大允許占50%,可以根據讀寫業務量來 進行調整。參數innodb_change_buffer_max_size
二、Log Buffer(日志緩沖區)
????用來保存要寫入磁盤上log文件(Redo/Undo)的數據。日志緩沖區的內容定期刷新到磁盤log文件中。日志緩沖區滿時會自動將其刷新到磁盤,當遇到BLOB 或多行更新的大事務操作時,增加日志緩沖區可以節省磁盤I/O
日志緩沖區大小由變量innodb_log_buffer_size配置,默認大小是16MB
????當Log Buffer如果較大,就可以存儲更多的Redo Log,這樣一來在事務提交之前我們就不需要將Redo Log刷入磁盤,只需要丟到Log Buffer中去即可。因此較大的Log Buffer就可以更好的支持較大的事務運行;同理,如果有事務會大量的更新、插入或者刪除行,那么適當的增大Log Buffer的大小,也可以有效的減少部分磁盤I/O操作。
????至于Log Buffer中的數據刷入到磁盤的頻率,則可以通過參數innodb_flush_log_at_trx_commit來決定。
關于InnoDB的結構這期先說到這里,我們下期接著聊一聊InnoDB的磁盤結構!
…………………………………分割線……………………………
不積跬步,無以至千里;不積小流,無以成江海。
關注我,每天分享一些小知識點。分享自己的小心得,包含但不限于初、中、高級面試題呦!!!
我都墨跡這么半天了 ,你不點關注,不點贊,不收藏,還不轉發,你想干啥!!!!