什么是MySQL的"內存數據加速器"——Buffer Pool?
我們都知道,MySQL 的數據(除了 Memory 引擎外)都存儲在磁盤上。然而,若每次查詢和修改都直接與磁盤交互,性能將會非常低下。
因此,為了提升讀寫性能,Innodb 引擎引入了一個中間層,即緩沖池(buffer pool)。
緩沖池是內存中一塊連續的空間,主要用于緩存數據頁。每個數據頁的大小為 16KB。
頁是 Innodb 進行數據存儲的基本單元,無論是在磁盤還是在緩沖池中,數據的讀取都是以頁為單位進行的,這也體現了一種“預讀”的思想。
圖片
有了緩沖池之后,當我們進行數據查詢時,InnoDB 會首先檢查緩沖池中是否存在該數據。如果存在,數據就可以直接從內存中獲取,避免了頻繁的磁盤讀取,從而提高查詢性能。如果不存在,則會去磁盤中讀取數據,并將找到的數據頁復制到緩沖池中,再返回給客戶端。這樣,后續的查詢可以直接從緩沖池中就近讀取數據。
圖片
當需要進行數據修改時,操作也會先在緩沖池中進行,然后再將修改后的數據寫入磁盤。
然而,由于緩沖池是基于內存的,其空間不可能無限大,默認大小為 128M。當然,這個大小并不是固定的,我們可以通過修改 MySQL 配置文件中的 innodb_buffer_pool_size 參數來調整緩沖池的大小。
# 查看buffer pool
SHOW VARIABLES LIKE 'innodb_buffer_pool_size';
# 修改buffer pool
SET GLOBAL innodb_buffer_pool_size = 512M;
擴展知識
InnoDB 的數據頁
上面提到了 InnoDB 的數據頁,它和 B+樹的關系是怎樣的呢?
InnoDB 的數據頁是其存儲引擎中用于存儲數據的基本單位。數據頁在磁盤上是一個連續的區域,通常大小為 16KB,當然也可以通過配置進行調整。16KB 意味著 InnoDB 的每次讀寫操作都是以 16KB 為單位的,即一次從磁盤讀取到內存的最小單位是 16KB,從內存寫入到磁盤的最小單位也是 16KB。
在 B+樹結構中,每個節點都對應著一個數據頁,包括根節點、非葉子節點和葉子節點。B+樹通過節點之間的指針連接不同層級的數據頁,從而構建出一個有序的索引結構。
圖片
通過 B+樹的搜索過程,可以從根節點開始逐層遍歷,最終到達葉子節點,找到所需的數據行。
因此,數據頁是存儲數據行的實際物理空間,以頁為單位進行磁盤讀寫操作。B+樹通過節點和指針的組織,構建了一個層次結構的索引,用于快速定位和訪問數據行。
B+樹的非葉子節點對應著數據頁,其中存儲著主鍵及指向子節點(即其他數據頁)的指針。B+樹的葉子節點包含實際的數據行,每個數據行存儲在一個數據頁中。
通過這種方式,InnoDB 利用 B+樹和數據頁的組合,實現了高效的數據存儲和檢索。B+樹提供了快速的索引查找能力,而數據頁提供了實際存儲和管理數據行的機制。它們相互配合,使得 InnoDB 能夠處理大規模數據的高效訪問。
數據頁的構成
一個數據頁包含七個部分,分別是文件頭、頁頭、最小和最大記錄、用戶記錄、空閑空間、頁目錄以及文件尾。
圖片
buffer pool 和 query cache 的區別
在 InnoDB 中,除了緩沖池(Buffer Pool),還有另一個緩存層用于數據緩存,提升查詢效率。很多人容易混淆它與緩沖池的區別。
首先,它們的目的和作用不同。緩沖池用于緩存表和索引的數據頁,從而加速讀取操作;而查詢緩存(Query Cache)用于緩存查詢結果,減少重復查詢的執行時間。
緩沖池主要與存儲引擎 InnoDB 相關,而查詢緩存也支持其他引擎,如 MyISAM 等。因此,查詢緩存位于服務器層的優化技術,而緩沖池位于引擎層的優化技術。
需要注意的是,在 MySQL 5.7 版本中,查詢緩存已經被標記為廢棄,并在 MySQL 8.0 版本中徹底被移除。
buffer pool 的讀寫過程是怎么樣的?
MySQL 的緩沖池(Buffer Pool)是一個內存區域,用于緩存數據頁,從而提高查詢性能。讀寫過程涉及將數據從磁盤讀取到內存、在內存中進行修改,并最終寫回磁盤。
讀過程
當我們在 MySQL 執行一個查詢請求時,其過程如下:
- MySQL 首先檢查緩沖池(Buffer Pool)中是否存在本次查詢的數據。如果數據在緩沖池中,就直接返回結果。
- 如果數據不在緩沖池中,MySQL 會從磁盤讀取數據。
- 讀取的數據頁被放入緩沖池,同時 MySQL 會將請求的數據返回給應用程序。
讀取過程相對簡單,而緩沖池的寫入過程則稍顯復雜。
寫過程
當執行一次更新語句(如 INSERT、UPDATE 或 DELETE)時,MySQL 的過程如下:
- 應用程序執行寫操作時,MySQL 首先將要修改的數據頁加載到緩沖池(Buffer Pool)中。
- 在緩沖池中,對數據頁進行修改,以滿足寫請求。這些修改只在內存中進行,不會立即寫回磁盤。
- 如果緩沖池中的數據頁被修改過,MySQL 會將這個頁標記為“臟頁”(Dirty Page)。
- 臟頁會被后臺線程寫回磁盤,這個過程稱為臟頁刷盤。寫入操作完成后,數據得以持久化。
需要注意的是,臟頁的寫回磁盤是由后臺線程進行的。在 MySQL 服務器空閑或負載較低時,InnoDB 會執行臟頁刷盤操作,以減少對用戶線程的影響,從而降低性能的影響。
參考文檔:https://dev.mysql.com/doc/refman/8.0/en/innodb-buffer-pool-flushing.html
圖片
當臟頁的百分比達到innodb_max_dirty_pages_pct_lwm變量定義的低水位標記時,將啟動緩沖池的刷新。緩沖池頁的默認低水位標記為 10%。將innodb_max_dirty_pages_pct_lwm值設為 0 會禁用這種提前刷新行為。
InnoDB 還采用了一種適應性刷新算法,根據 redo log 的生成速度和當前的刷新率動態調整刷新速度。其目的是通過確保刷新活動與當前工作負載保持同步,來平滑整體性能。
當然,我們也可以通過執行SET GLOBAL innodb_buffer_pool_dump_now=ON來手動觸發臟頁刷新到磁盤。
此外,在 MySQL 服務器正常關閉或重啟時,所有的臟頁都會被刷新到磁盤,以確保數據持久化。