每16kb為一頁,連續64個頁就是一個區,默認占用1MB,每256個區被划分成一個組。
LRU鏈表,淘汰算法
LRU(Least Recentiy Used),最近最少使用。
為了防止空閑的緩存頁不夠用,需要將一些緩存頁刷回到磁盤,但是有些緩存頁使用很頻繁有些就不經常使用,所以需要將最近不經常使用到的緩存頁刷回到磁盤,LRU鏈表就是存放最近很少使用的緩存頁的。
當把數據頁加載到緩存頁的時候就把這個緩存頁的描述數據塊放到LRU鏈表的頭部,所有剛被加載到緩存頁的描述數據塊都被放到頭部,而且后續如果查詢或者修改了某一個緩存頁里的數據也將其放到LRU鏈表的頭部。這樣的話LRU鏈表的尾部一定就是最少被使用的緩存頁了。
MySQL預讀機制
線性預讀(linear read-ahead)。參數innodb_read_ahead_threshold,默認值是56,意思是當加載數據頁時,順序的訪問了一個區里的多個數據頁,如果訪問的數據頁的數量超過了這個閾值就會觸發預讀機制,會把相鄰的區中的所有數據頁都加載到緩存中。
隨機預讀(randomread-ahead)。innodb_random_read_ahead參數,默認時OFF,也就是關閉的。當這個規則打開時,如果buffer pool里緩存了一個區中連續的13個數據頁而且這些都是被經常訪問的話,就會將這個區其他的數據頁都加載到緩存中去。在5.5中已經將這種預讀方式廢棄,默認是OFF。若要啟用此功能,即將配置變量設置innodb_random_read_ahead為ON。
這時,通過預讀機制加載到buffer pool的數據如果都放到LRU鏈表的頭部會導致其他被經常訪問的數據移動到尾部從而被淘汰刷回到磁盤,這是很不合理的,因為通過預讀機制加載的數據可能不會用到。除了通過預讀機制導致頻繁被訪問的緩存頁被淘汰之外全表掃描也會導致這個情況,比如select * from xxx,不加where條件。
冷熱數據分離的LRU
實際上LRU鏈表會被分為兩個部分,熱數據部分(young區域)和冷數據部分(old區域)。每個部分所占的比例可以通過innodb_old_blocks_pct參數來指定,默認為37,意思是冷數據占比37%。實際上數據第一次被加載到緩存的時候是放在冷數據區鏈表的頭部。當被加載到冷數據區的頭部之后經過指定的時間后被訪問了的數據就會被移動到熱數據區的鏈表頭部。這個指定時間是通過innodb_old_blocks_time指定的,默認為1000ms也就是1秒。只有當被訪問的緩沖頁位於熱數據區(young區域)后面的3/4的數據時,才會被移動到LRU鏈表的頭部。(降低調整LRU鏈表的頻率,從而提升性能
刷盤時機
mysql后台線程會定時的把LRU鏈表冷數據區尾部的一些緩存頁刷回到磁盤,並將其從LRU鏈表去除加入到free鏈表。除此之外這個后台線程還會在MySQL不怎么繁忙的時候將flush鏈表中存放的臟數據刷回到磁盤並將其從LRU鏈表中去除加回到free鏈表。當實在沒有空閑的緩存頁的時候就會將LRU鏈表冷數據區尾部的緩存頁刷回到磁盤,空出緩存頁,再把用到的數據從磁盤加載到緩存,但是這就有了兩次磁盤IO,如果很頻繁的這樣操作會很影響性能。
緩存命中率越高說明基於緩存的操作就越多,性功越高。show engine innodb status命令可以查看當前innodb里的情況
