MySQL中的Change Buffer

Change Buffer

當須要更新一個數據頁時,若是數據頁在內存中的話,則直接更新。而若是這個數據頁不在內存中的話, InnoDB會把這些操做緩存在Change Buffer中。這樣就不須要從磁盤讀取數據了。在下次查詢須要訪問這個數據頁的時候,將數據頁讀入內存,而後執行 change buffer 中與這個頁有關的操做。經過這種方式就能保證這個數據邏輯的正確性。mysql

將 change buffer 中的操做應用到原數據頁,獲得最新結果的過程稱爲 merge。除了訪問這個數據頁會觸發 merge 外,系統有後臺線程會按期 merge。在數據庫正常關閉(shutdown)的過程當中,也會執行 merge 操做。sql

顯然,若是可以將更新操做先記錄在 change buffer,減小讀磁盤,語句的執行速度會獲得明顯的提高。並且,數據讀入內存是須要佔用 buffer pool 的,因此這種方式還可以避免佔用內存,提升內存利用率。數據庫

使用Change Buffer的狀況

對於惟一索引來講,全部的更新操做都要先判斷這個操做是否違反惟一性約束。好比,要插入 (4,400) 這個記錄,就要先判斷如今表中是否已經存在 k=4 的記錄,而這必需要將數據頁讀入內存才能判斷。若是都已經讀入到內存了,那直接更新內存會更快,就不必使用 change buffer 了。緩存

Change Buffer案例

若是要在這張表中插入一個新記錄 (4,400) 的話,InnoDB 的處理流程是怎樣的。bash

第一種狀況:性能

要插入的數據頁在內存中,又分爲兩種狀況spa

  • 一是對於惟一索引, 找到3到5之間的位置,判斷是否有衝突。若是沒有,則直接插入。
  • 二是對於普通索引來講,找到3到5之間的位置,則直接插入。

第二種狀況:線程

  • 對於惟一索引來講,須要將數據頁讀入內存,判斷到沒有衝突,插入這個值,語句執行結束;
  • 對於非惟一索引來講,直接將記錄更新在change buffer中,直接結束。

將數據從磁盤讀入內存涉及隨機 IO 的訪問,是數據庫裏面成本最高的操做之一。change buffer 由於減小了隨機磁盤訪問,因此對更新性能的提高是會很明顯的。3d

change buffer 的使用場景

經過上面的分析,你已經清楚了使用 change buffer 對更新過程的加速做用,也清楚了 change buffer 只限於用在普通索引的場景下,而不適用於惟一索引。那麼,如今有一個問題就是:普通索引的全部場景,使用 change buffer 均可以起到加速做用嗎?日誌

由於 merge 的時候是真正進行數據更新的時刻,而 change buffer 的主要目的就是將記錄的變動動做緩存下來,因此在一個數據頁作 merge 以前,change buffer 記錄的變動越多(也就是這個頁面上要更新的次數越多),收益就越大。所以,對於寫多讀少的業務來講,頁面在寫完之後立刻被訪問到的機率比較小,此時 change buffer 的使用效果最好。這種業務模型常見的就是帳單類、日誌類的系統。

反過來,假設一個業務的更新模式是寫入以後立刻會作查詢,那麼即便知足了條件,將更新先記錄在 change buffer,但以後因爲立刻要訪問這個數據頁,會當即觸發 merge 過程。這樣隨機訪問 IO 的次數不會減小,反而增長了 change buffer 的維護代價。因此,對於這種業務模式來講,change buffer 反而起到了反作用。

change buffer 和 redo log

假設須要在表上執行以下語句

mysql> insert into t(id,k) values(id1,k1),(id2,k2);
複製代碼

這裏,咱們假設當前 k 索引樹的狀態,查找到位置後,k1 所在的數據頁在內存 (InnoDB buffer pool) 中,k2 所在的數據頁不在內存中。如圖 2 所示是帶 change buffer 的更新狀態圖。

這條更新語句作了以下的操做(按照圖中的數字順序): Page 1 在內存中,直接更新內存;

age 2 沒有在內存中,就在內存的 change buffer 區域,記錄下「我要往 Page 2 插入一行」這個信息將上述兩個動做記入 redo log 中

同時,圖中的兩個虛線箭頭,是後臺操做,不影響更新的響應時間。

那在這以後的讀請求,要怎麼處理呢?好比,咱們如今要執行 select * from t where k in (k1, k2)。

讀 Page 1 的時候,直接從內存返回。有幾位同窗在前面文章的評論中問到,WAL 以後若是讀數據,是否是必定要讀盤,是否是必定要從 redo log 裏面把數據更新之後才能夠返回?實際上是不用的。你能夠看一下圖 3 的這個狀態,雖然磁盤上仍是以前的數據,可是這裏直接從內存返回結果,結果是正確的。要讀 Page 2 的時候,須要把 Page 2 從磁盤讀入內存中,而後應用 change buffer 裏面的操做日誌,生成一個正確的版本並返回結果。能夠看到,直到須要讀 Page 2 的時候,這個數據頁纔會被讀入內存。

redo log 主要節省的是隨機寫磁盤的 IO 消耗(轉成順序寫),而 change buffer 主要節省的則是隨機讀磁盤的 IO 消耗。

相關文章
相關標籤/搜索