LRU算法及其優化策略——Mysql篇

LRU算法及其優化策略——Mysql篇.jpg

上一篇文章中,介紹了LRU算法在Redis之中的應用,本篇繼續給各位道友介紹在Mysql的InnobDB引擎中,是如何使用LRU算法的。html

InnoDB緩衝池

緩存池簡介及內存結構

首先來介紹下InnoDB的緩衝池,緩衝池簡單來講就是一塊內存區域,該區域內緩存着InnoDB訪問存儲在磁盤的數據和索引信息。緩衝池有兩個做用,一是提升了大容量讀取操做的效率,二是提升了緩存管理的效率。調配緩存池參數,使得常常訪問的參數可以保留在緩存池中是一個很重要的Mysql優化手段。mysql

一個InnoDB緩存池的內存結構圖以下圖所示:算法

緩衝池.png

圖源自《Mysql技術內幕:InnoDB存儲引擎》sql

緩存池狀態

咱們能夠經過SHOW ENGINE INNODB STATUS命令來查看緩存池在InnoDB引擎中的表現:shell

----------------------
BUFFER POOL AND MEMORY
----------------------
Total memory allocated 6593445888;                       // 爲緩衝池分配的總內存(字節)
Dictionary memory allocated 7687783                      // 爲InnoDB數據字典分配的總內存(字節)
Buffer pool size   393208                                // 分配給緩衝池的頁面總大小(頁)
Free buffers       352642                                // 緩衝池空閒列表的頁面總大小(頁)
Database pages     40485                                 // 緩衝池LRU列表的頁面總大小。(頁)
Old database pages 14967                                 // 緩衝池舊LRU子列表的頁面總大小(頁)
Modified db pages  4                                     // 緩衝池中當前修改的頁面數。
Pending reads 0                                          // 等待讀入緩衝池的緩衝池頁面數。
Pending writes: LRU 0, flush list 0, single page 0       // 從LRU列表的底部開始寫入的緩衝池中的舊髒頁數。                                                          // 檢查點期間要刷新的緩衝池頁面數。
                                                         // 緩衝池中暫掛的獨立頁面寫入數。
Pages made young 5, not young 0                          // 緩衝池LRU列表中變年輕的頁面總數
                                                         // 緩衝池LRU列表中未設置爲年輕的頁面總數
...
複製代碼

完整的緩存池狀態信息能夠在這裏找到:緩存池狀態信息數據庫

緩存池的數量和大小

爲了不多個線程讀寫緩存池引發的併發衝突,InnoDB能夠配置多個緩存池,由參數innodb_buffer_pool_instances指定,內部使用散列表進行分配和管理。緩存

一般來講,當緩存池的大小越大,則Mysql表現的越像一個內存數據庫。咱們能夠在啓動時或者運行時經過innodb_buffer_pool_size參數動態地調整緩存池的大小,須要注意的innodb_buffer_pool_size的大小會自動的調整爲InnoDB緩存池塊innodb-buffer-pool-chunk-size(默認爲128M)的整倍數。併發

爲避免潛在的性能問題,緩存池大小/緩存池塊大小(innodb_buffer_pool_size/ innodb_buffer_pool_chunk_size)的數量不該超過1000。dom

緩存池的刷新

說到緩存,必須有緩存刷新機制,即剔除緩存中的髒頁(已經被修改,可是並未刷入磁盤中的數據頁)。異步

在5.7以上的版本中,InnoDB會啓動默認四個線程併發的來執行緩存池中髒頁的清除。髒頁的清除有兩種模式:

  1. 普通模式,當緩存池中的髒頁比例超過innodb_max_dirty_pages_pct_lwm(低水平線默認爲25%)時,啓動普通模式將髒頁刷新到磁盤中。
  2. aggressively flushes(激進模式?),當緩存池中的髒頁比例超過innodb_max_dirty_pages_pct(默認爲75%)時,啓動更快的刷新模式,儘快的將髒頁刷新到磁盤當中。

緩存池的預讀(Prefetching )

InnoDB的緩存池不只是被動地緩存,並且會異步地預先從磁盤中讀取數據頁,有兩種方式:

  • 線性:根據緩存池的訪問數據的順序來預讀,當讀取某一區(Extend)中的頁(Page)的數據超過innodb_read_ahead_threshold時,則將該區中剩餘的全部頁都加載到緩存池中。

  • 隨機:根據緩存池中的已有頁面進行預讀,而無論他們的順序,當發現緩存池中某一區內頁的數量超過了innodb_random_read_ahead,則將改區中剩餘的全部頁都加載到緩存池中。

緩存池LRU算法

在瞭解了InnoDB的緩存池概念後,咱們來看看背後支持緩存池工做的算法。

當咱們使用樸素的LRU算法時,會發現若是有批量的操做時,會打亂緩存數據,大大下降了緩存命中率。而在Mysql當中會有大量的預讀及全表掃描的操做,爲了使得真真的熱數據留在內存中,InnoDB緩存池採用了一種變種的LRU算法,有些像我在這篇文章中寫到的LRU-K算法。

新進入緩存池的頁並不會直接進入LRU鏈表的頭部,而是插入到距離鏈表尾3/8的位置(能夠由innodb_old_blocks_pct參數進行配置),咱們將距離鏈表尾3/8以上的位置稱爲新子列表,如下的位置稱爲舊子列表,數據在鏈表中自底而上稱爲變年輕,反之稱爲變老。下圖是一個示意圖:

innodb-buffer-pool-list.png

  • 變年輕

    變年輕分爲兩種狀況,第一種是來源於用戶的操做而須要讀取頁面,此時會直接使該頁直接移至新子列表鏈表頭部。第二種是來源於數據庫內部的預讀操做,則在距離插入innodb_old_blocks_time(默認爲1000ms)的時間內,即便訪問了該頁,該頁也不會別移到LRU鏈表的頭部。

    也就是說,若是是來源於用戶的操做,則最起碼須要兩次操做才能變年輕。而若是是預讀操做,則須要加上一個等待期限。

  • 變老

    隨着鏈表數據的替換和訪問,整個列表中的數據會天然的變老。最終最老的頁面會從尾部逐出。

總結

本文介紹了Mysql的InnoDB引擎的緩存池的概念,及其對於LRU算法的改造。介紹了另外一種解決LRU列表被污染的解決方案。

相關文章
相關標籤/搜索