1. 程式人生 > >【大白話系統】MySQL 學習總結 之 緩衝池(Buffer Pool) 如何支撐高併發和動態調整

【大白話系統】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 數量符合上面的公式,其實都是可以的,看你們根據業務後怎麼選擇了