帶你了解MySQL中的資料庫緩衝池(Buffer Pool)

青灯夜游
發布: 2023-02-21 19:44:06
轉載
2290 人瀏覽過

帶你了解MySQL中的資料庫緩衝池(Buffer Pool)

對於使用InnoDB儲存引擎的表來說,是以頁為單位來管理儲存空間的,作為記憶體和磁碟之間換入換出的基本粒度。當我們將某頁從磁碟載入到記憶體中,就會進行磁碟I/O。而磁碟I/O的開銷非常影響整體效能,如果我們直接從記憶體中讀取對應的頁,那豈不是減少了磁碟I/O帶來的效能損耗,效率會提升很多。基於此,緩衝池(Buffer Pool 出現了,那麼接下來,我們就來談談InnoDB中的Buffer Pool。

緩衝池(Buffer Pool)

有人會想,既然緩衝池這麼好,那我們將所有資料都儲存到緩衝池中不就好了,不不不,緩衝池是作業系統分配的一片連續的記憶體。而記憶體相比於磁碟的容量小得多,並且價格昂貴。那麼作業系統會給緩衝池分配多少記憶體呢?

  • 預設情況下,緩衝池的大小為128MB;

#當然,如果你的機器的記憶體容量非常大,可以在設定檔中設定啟動選項參數innodb_buffer_pool_size單位是位元組,最小不能小於5MB。

緩衝池的內部結構

緩衝池將作業系統分配的這一連續的內存,劃分成若干個大小預設為16KB的頁(緩衝頁)【此時還沒有真正的磁碟頁被快取到Buffer Pool中】,當我們從磁碟換入一個頁到緩衝池中,如何分配位置呢?因此就需要一些控制資訊來標識這些緩衝池中的緩衝頁,這些控制資訊都存放在一個叫做控制塊的記憶體區域中,與緩衝頁一一對應。控制塊的大小也是固定的。因此在這片連續的記憶體空間中,難免會產生記憶體碎片。綜上,緩衝池的內部結構如下:

  • 緩衝頁
  • 控制區塊:頁號、緩衝頁在緩衝池中的位址、鍊錶節點資訊等。
  • 記憶體碎片【若記憶體分配得當,記憶體碎片可有可無】

帶你了解MySQL中的資料庫緩衝池(Buffer Pool)

緩衝池的管理

上面在控制塊中提到了鍊錶節點訊息,那麼鍊錶節點是用來做什麼的呢?是為了更好的管理緩衝池中的頁。而鍊錶就是用來連結控制塊的,因為控制塊與緩衝頁是一一對應的。

1)空閒鍊錶

將所有空閒的緩衝頁對應的控制區塊連結起來,形成的鍊錶。

解決的問題:從磁碟換入一個頁到緩衝池中,如何區分緩衝池中的哪個頁是空閒的呢?而有了空閒鍊錶之後,換入一個磁碟頁到緩衝池中時,就直接從空閒鍊錶中取得一個空閒的緩衝頁,並將磁碟頁中對應的資訊填到緩衝頁對應的控制塊中,然後將該控制塊從空閒鍊錶中刪除即可。

2)更新鍊錶

若修改了緩衝池中的緩衝頁的數據,導致其與磁碟中資料不一致,該頁稱為髒頁。將所有髒頁對應的控制區塊連結起來形成更新鍊錶,在將來的某個時間根據該鍊錶將對應快取頁的資料刷新到磁碟中。

3)LRU鍊錶

緩衝池的大小是有限的,如果快取的頁超出了緩衝池的大小,即沒有空閒的緩衝頁了,當有新的頁要添加到緩衝池時,採取LRU的策略將舊的緩衝頁從緩衝池中移除,然後將新的頁加入進來。由於LRU鍊錶涉及的內容較多,我們接下來單獨介紹。

LRU鍊錶所蘊含的「哲理」

先提一下預讀機制

在I/O上的最佳化機制,預讀顧名思義,會異步地把某有些頁面載入到緩衝池中,預計很快就會需要這些頁面,這些請求在一個範圍內引入所有頁面,就是所謂的局部性原理,目的是減少磁碟I/O。

在了解預讀機制之前,先回顧InnoDB邏輯儲存單元:表空間(tablespace)→段(segment )→區(extent)→頁(page)。其中特意提一下區,後面會用到:一個區就是物理位置上連續的64頁,也就是一個區的大小是1MB.

帶你了解MySQL中的資料庫緩衝池(Buffer Pool)

##預讀機制可以細分為以下兩種:

  • Linear read-ahead(線性預讀):一種基於依序存取的緩衝池中的頁面來預測可能很快需要哪些頁面的技術。透過設定參數innodb_read_ahead_threshold,若順序存取的某個區的頁面超過這個參數的值,會觸發非同步讀取請求來讀取下一個區中全部的頁面到緩衝池中。
  • Random read-ahead(隨機預讀):可以根據緩衝池中已經存在的頁面預測何時可能需要頁面,而不管這些頁面的讀取順序如何。如果在緩衝池中發現同一個區段的13個連續頁面,InnoDB會非同步發出一個請求來預取該區段的剩餘頁面。透過配置變數innodb_random_read_ahead來控制隨機讀取的。

傳統LRU對緩衝頁是如何管理的呢?

利用LRU演算法對最近最少使用的緩衝頁進行管理,形成對應的鍊錶,方便用於淘汰。

當訪問一個頁【即最近訪問】

  • 該頁在緩衝池中,將對應控制塊移至LRU鍊錶頭部
  • 該頁不在緩衝池中,淘汰尾部最近最少使用的頁,從磁碟中加載進來該頁並放在LRU鍊錶頭部

那麼為什麼InnoDB不使用這麼直觀的LRU演算法呢?原因如下:

  1. 預讀失效

    預先閱讀緩衝池中的頁都會放到LRU鍊錶的頭部,但其中很多頁可能並不會被讀取。

  2. 緩衝池污染

    很多使用頻率較低的頁載入到緩衝池中,會把使用頻率較高的頁從緩衝池中淘汰掉。例如全表掃描

優化後的LRU對緩衝頁是如何管理的呢?

基於上述缺點,優化後的具體方法將傳統LRU鍊錶劃分為兩部分:熱資料區域【年輕區】&冷資料區域【老年區】

  • 熱資料區【年輕區】:使用頻率高的緩衝區頁
  • 冷資料區【老年區】:使用頻率低的區域

#結構簡圖如下所示:

如圖所示,熱資料區域與冷資料區域分別佔用不同比例,那麼我們可以透過innodb_old_blocks_pct啟動選項來控制冷資料區域所佔比例。

帶你了解MySQL中的資料庫緩衝池(Buffer Pool)

改進後的LRU如何更好的解決預讀失敗問題呢?

  • 某頁在初次載入到緩衝池時,先淘汰掉冷資料區域尾部的控制區塊(即其對應的頁淘汰掉),然後新頁對應的控制區塊會先放到冷資料區域的頭部。
  • 若後續該頁不被進行存取就會慢慢從冷資料區域中被淘汰掉,整體不會影響熱資料區域存取頻繁的緩衝頁。

改進後的LRU如何更好的解決緩衝池污染問題呢?

先說結論,並沒有很好的最佳化這個問題,原因如下【以全表掃描為例】:

  • 某個初次造訪的頁同樣會放到冷資料區域的頭部,但後續訪問又會將其放到熱資料區域的頭部,這樣同樣會把存取頻率較高的頁給擠掉。

那麼到底該如何解決緩衝池污染問題呢?

  • 緩衝池引入了冷資料區域時間視窗機制,即只有後續存取該頁與第一存取該頁的時間間隔大於規定的視窗值,就會將該頁從冷資料區域移到熱數據區域的頭部。小於規定的視窗值,就不會進行移動操作。
  • 同樣,視窗值可透過innodb_old_blocks_time參數【單位ms】來設置,預設1000ms,而1s會篩選掉大部分像全表掃描這樣的操作。例如在一次全表掃描過程中,多次造訪一個頁面的時間間隔不會超過1s。

緩衝池VS查詢快取

緩衝池和查詢快取是一個東西嗎? →不是

  • 緩衝池會盡量將經常使用的資料保存起來,在MySQL進行頁面讀取操作的時候,首先會判斷該頁面是否在緩衝池中,如果存在就直接讀取,如果不存在,就會透過記憶體或磁碟將頁面存放到緩衝池中再進行讀取。
  • 查詢快取是提前把查詢結果快取起來,這樣下次就不需要執行就可以直接拿到結果。需要說明的是,在MySQL中的查詢緩存,不是緩存查詢計劃,而是查詢對應的結果。命中條件苛刻,而且只要資料表發生變化,查詢快取就會失效,因此命中率低。

【相關推薦:mysql影片教學

以上是帶你了解MySQL中的資料庫緩衝池(Buffer Pool)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:juejin.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板
關於我們 免責聲明 Sitemap
PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!