最近在準備給新人教育訓練,對于DBA相關的新人,最推薦的莫過于學習官方文檔,像Oracle的concept,MySQL的reference等,MySQL被Oracle收購以後,文檔也越來越完整和形象,對于剛學習的新人,學習官方文檔有助于跟為系統得學習。
本篇主要講MySQL8.0InnoDB的架構。
跟其他關系型資料庫一樣,MySQL的架構也分為In-Memory 和On-Disk兩部分。

在記憶體結構中,主要由4大塊組成。
Buffer Pool(以下簡稱bp)
bp在整個記憶體中占絕大多數,MySQL官方一般建議設定為伺服器實體記憶體的80%。這邊建議MySQL整體記憶體控制在80%左右,bp最好不要超過70%,不然容易OOM。
了解了bp的原理,對于資料庫也有差不多了解了一半了。bp主要結構是個list,用的是LRU算法的變種(least recently used)。
主要用來緩存資料塊(page,也叫頁),mysql預設為16k,oracle為8k。如果記憶體達到了門檻值,那麼就會把最近最少使用到的page驅逐(evicted)出記憶體。然後把新頁插入到中間位置,一般為整條list的熱端5/8處。
檢視下圖可以看到,整個bp分為兩個sublist,上端為熱端,緩存熱點資料(頻繁被通路);另外一端為冷端,随時被驅逐出記憶體。
也正是因為磁盤通路資料不如記憶體,才有了bp的存在,大家也可以想象一下,如果磁盤資料比記憶體快,還有bp存在的價值嗎?
所有使用者通路的資料,都要先經過bp,但如果走全表掃描,按道理也有把資料緩存在bp中,那麼勢必會把大量資料驅逐出去,當然我們也可以通過調整參數來優化,這個我們在這篇知識普及文檔中暫不提及。
可以通過設定 innodb_buffer_pool_size來調整bp的大小,可以在執行個體啟動前在my.cnf中配置,也可以在執行個體運作中動态設定。
并且可以通過設定innodb_buffer_pool_instances的數量來調整bp池的數量,有利于減少LRU mutex的争議和掃描量,最近剛好有碰到這個bug:
https://bugs.mysql.com/bug.php?id=98869。如果是運作中動态設定bp,還可以通過監控 Innodb_buffer_pool_resize_status參數來檢視resize的狀态(也同樣會寫到error log中);如果是增大bp,則會增加chunks數量和page,覆寫hash table和list,并且指針指向記憶體中新的位址,增加新頁到free list;如果是減少bp,則釋放和重組bp,在chunks中移除page,覆寫hash table和list,并且指針指向記憶體中新的位址。
可以通過運作: SHOW ENGINE INNODB STATUS,在BUFFER POOL AND MEMORY部分看到bp相關統計資料。
附:
https://dev.mysql.com/doc/refman/8.0/en/innodb-buffer-pool.htmlChange Buffer(以下簡稱cb)
cp是mysql記憶體結構中比較特殊的一個,它是用來緩存不在bp中的二級索引(secondary index,相對于cluster index來說)變化的page,是DML的結果,并定期合并、讀到bp中。二級索引可以值是不唯一的,cp也是占用bp的記憶體,在shutdown 執行個體以後,會成為系統表空間的一部分。cp存在的意義是為了在執行DML的時候,異步二級索引的寫入,到達盡可能的順序寫,提高IO效率,對I/O-bound型有一定優化作用。可以通過設定 innodb_change_buffering來控制什麼類型的DML能夠在cp中。通過innodb_change_buffer_max_size來設定在bp中占用的百分比。可以通過show engine innodb status中的INSERT BUFFER AND ADAPTIVE HASH INDEX 來監控cp相關資料統計。
Adaptive Hash Index
mysql本身不支援哈希索引,但内部有使用到自适哈希索引,維護hash表。可以通過 innodb_adaptive_hash_index 來啟用,也可以動态修改。
hash index通過B-TREE索引鍵字首建立,基于需求和最經常通路的搜尋pattern。mysql基于自己的算法來自行建立哈希索引,并且hash table是分區的,通過控制innodb_adaptive_hash_index_parts來設定分區數量,在特定情況下可以啟動加速查詢的效果,對like和%不友好。
Redo Log Buffer
redo log buffer也是基本所有關系型資料庫都有的,redo log也叫 WAL,用來緩存資料變化,在髒資料flush到資料檔案前,需要先寫入到redo log buffer,再寫入到redo log中,一個目的是用來保證資料一緻性,保證資料庫crash以後能夠通過redo log進行恢複。第二個主要用來降低IO,如果每次資料庫的DML都直接寫入到資料檔案中,則會大大影響性能。可以通過innodb_log_buffer_size來控制記憶體大小,預設為16M。定期有線程對齊進行flush,確定更新都刷入到redo log中,并且redo log的page size為512k,也就是一個扇區的大小,并且是順序寫,保證了性能和一緻性,不至于需要double write.通過設定innodb_flush_log_at_trx_commit=1來獲得ACID的保證,每次事務送出馬上flush到redo log,如果對資料一緻性不嚴格的,可以通過設定0來定期重新整理,提高性能。定期重新整理可以通過設定innodb_flush_log_at_timeout來控制,預設為1秒鐘。
至此mysql記憶體相關的4個主要的組成部分已經講完,另外還有很多小的記憶體區域,比如由來緩存資料字典等,以後碰到再講,接着會介紹mysql on-disk部分的架構。
https://yq.aliyun.com/articles/764516?spm=a2c4e.11155435.0.0.71de331269m7Gf