【大白話系列】MySQL 學習總結 之 緩衝池(Buffer Pool) 如何支撐高併發和動態調整

如果大家對我的 【大白話系列】MySQL 學習總結系列 感興趣的話,可以點擊關注一波。

一、上節回顧

在上節《 緩衝池(Buffer Pool) 的設計原理和管理機制》中,介紹了緩衝池整體的設計原理。包括幾個比較重要的概念:free 鏈表、flush 鏈表和 lru 鏈表。正式因爲這一套機制,使得 InnoDB 存儲引擎可以基於內存操作,避免了磁盤隨機讀寫的低性能。

二、Buffer Pool 如何應對高併發場景

1、單個 Buffer Pool 的問題

直到現在,估計大家都以爲緩衝池只是一個大的內存區域,在 InnoDB 存儲引擎中只有一個,這是對的嗎?

我們可以想象,如果 InnoDB 存儲引擎只有一個 Buffer Pool,當高併發時,多個請求進來,那麼爲了保證數據的一致性(緩存頁、free 鏈表、flush 鏈表、lru 鏈表等多種操作),必須得給緩衝池加鎖了,每一時刻只能有一個請求獲得鎖去操作 Buffer Pool,其他請求只能排隊等待鎖釋放。那麼此時 MySQL 的性能是多麼的低!

2、多個 Buffer Pool

既然一個 Buffer Pool 不夠用,那麼整多幾個唄。

在生產環境中,其實我們是可以給 MySQL 設置多個 Buffer Pool 來提升 MySQL 的併發能力的~

如何設置?

我們先看看 MySQL 的默認規則:如果你給 Buffer Pool 分配的內存小於1GB,那麼最多就只會給你一個 Buffer。

但是呢,如果你給 MySQL 設置的內存很大,此時你可以利用下面兩個參數來設置 Buffer Pool 的總大小和總實例數,這樣,MySQL 就能有多個 Buffer Pool 來支撐高併發了。

[server] 
innodb_buffer_pool_size = 8589934592 
innodb_buffer_pool_instances = 4

解釋一下:上面利用參數 innodb_buffer_pool_size 來設置 Buffer Pool 的總大小爲 8G,利用參數 innodb_buffer_pool_instances 來設置一共有 4個 Buffer Pool 實例。那麼就是說,MySQL 一共有 4個 Buffer Pool ,每個的大小爲 2G。

當然了,每個 Buffer Pool 負責管理着自己的描述數據塊和緩存頁,有自己獨立一套 free 鏈表、flush 鏈表和 lru 鏈表。

到這,我們就曉得,只要你能分配足夠大的內存給 Buffer Pool ,你就能創建儘量多的 Buffer Pool 來應對高併發場景~

正所謂,併發性能不高,機器配置來湊,這還是有道理的。

但是正經點來說,最基本最主要的還是咱們寫的 SQL。當然了,能寫出一手好 SQL,前提我們還是得理解 MySQL 各個組件的原理,熟悉索引的原理、事務原理和鎖原理等。當然了,之後我也會分別對這些做出一個學習總結分享出來。

三、生產環境中,如何動態調整 Buffer Pool 的大小

相信基本每個公司,項目上線後,用戶和流量會不斷地增長,這對於 MySQL 來說,會有什麼變化?

首先,訪問增多,不斷地從磁盤文件中的數據頁讀取數據到 Buffer Pool,也不斷地將 Buffer Pool 的髒緩存頁刷回磁盤文件中。很明顯的,Buffer Pool 越小,這兩個操作就會越頻繁,但是磁盤IO操作又是比較耗時的,本來 SQL 執行只要 20 ms,如果碰巧碰到遇到緩存頁用完,就要經歷一系列的操作,SQL 最後執行完可能就要 200 ms,甚至更多了。

所以我們此時需要及時調整 Buffer Pool 的大小。

1、如何動態調整 Buffer Pool 的大小?

但是生產環境,肯定不能讓我們直接修改 MySQL 配置然後再重啓吧,這估計要罵死。

在 MySQL 5.7 後,MySQL 允許我們動態調整參數 innodb_buffer_pool_size 的值來調整 Buffer Pool 的大小了。

假如就這樣直接調大會存在啥問題?

假設調整前的配置:Buffer Pool 的總大小爲8G,一共4個 Buffer Pool,每個大小爲 2G。

[server] 
innodb_buffer_pool_size = 8589934592 
innodb_buffer_pool_instances = 4

假設給 Buffer Pool 調整到 16 G,就是說參數 innodb_buffer_pool_size 改爲 17179869184。

此時,MySQL 會爲 Buffer Pool 申請一塊大小爲16G 的連續內存,然後分成 4塊,接着將每一個 Buffer Pool 的數據都複製到對應的內存塊裏,最後再清空之前的內存區域。

我們可以發現,全部數據要從一塊地方複製到另外一塊地方,那這是相當耗費時間的操作,整整8個G的數據要進行復制粘貼呢!而且,如果本來 Buffer Pool 是更大的話,那就更恐怖了。

2、Buffer Pool 的 chunk 機制

爲了解決上面的問題,Buffer Pool 引入一個機制:chunk 機制。

  1. 每個 Buffer Pool 其實是由多個 chunk 組成的。每個 chunk 的大小由參數 innodb_buffer_pool_chunk_size 控制,默認值是 128M。
  2. 每個 chunk 就是一系列的描述數據塊和對應的緩存頁。
  3. 每個 Buffer Pool 裏的所有 chunk 共享一套 free、flush、lru 鏈表。

得益於 chunk 機制,就能避免了上面說到的問題。當擴大 Buffer Pool 內存時,不再需要全部數據進行復制和粘貼,而是在原本的基礎上進行增減內存。

下面繼續用上面的例子,介紹一下 chunk 機制下,Buffer Pool 是如何動態調整大小的。

調整前 Buffer Pool 的總大小爲 8G,調整後的 Buffer Pool 大小爲 16 G。

由於 Buffer Pool 的實例數是不可以變的,所以是每個 Buffer Pool 增加 2G 的大小,此時只要給每個 Buffer Pool 申請 (2000M/128M)個chunk就行了,但是要注意的是,新增的每個 chunk 都是連續的128M內存。

四、生產環境中,應該給 Buffer Pool 設置多大的內存

1、如何設置 Buffer Pool 的總內存大小

我們都知道,給 Buffer Pool 分配越大的內存,MySQL 的併發性能就越好。那是不是都應該將百分之九十九的機器的內存都分配給 Buffe Pool 呢?

那當然不是了!

先不說操作系統內核也需要幾個G內存,MySQL 除了 Buffer Pool 還有很多別的內存數據結構呢,這些都是需要內存的,所以說,上面的想法是絕對不行的!

比較合理的比例,應該是 Buffer Pool 的內存大小佔機器總內存的 50% ~ 60%,例如機器的總內存有32G,那麼你給 Buffer Pool 分配個20G左右就挺合理的了。

2、如何設置 Buffer Pool 的實例數

Buffer Pool 的總大小搞定了,那應該設置多少個實例數呢?

這裏有一個公式:Buffer Pool 總大小 = (chunk 大小 * Buffer Pool數量) n倍*

上個例子解釋一下。

假設此時 Buffer Pool 的總大小爲 8G,即 8192M,那麼 Buffer Pool 的數量應該是多少個呢?

8192 = ( 128 * Buffer Pool 數量)* n

64 個:也是可以的,但是每個 Buffer Pool 就只要一個 chunk。

16 個:也是可以的,每個 Buffer Pool 擁有四個 chunk。

8 個:也是可以的,每個 Buffer Pool 擁有八個 chunk。

所以說,只要你的 Buffer Pool 數量符合上面的公式,其實都是可以的,看你們根據業務後怎麼選擇了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章