重要,知識點:InnoDB的插入緩衝

世界上最快的捷徑,就是腳踏實地,本文已收錄【 架構技術專欄】關注這個喜歡分享的地方。

InnoDB引擎有幾個重點特性,爲其帶來了更好的性能和可靠性:數據庫

  • 插入緩衝(Insert Buffer)
  • 兩次寫(Double Write)
  • 自適應哈希索引(Adaptive Hash Index)
  • 異步IO(Async IO)
  • 刷新鄰接頁(Flush Neighbor Page)

今天咱們的主題就是 插入緩衝(Insert Buffer),因爲InnoDB引擎底層數據存儲結構式B+樹,而對於索引咱們又有彙集索引和非彙集索引。數據結構

在進行數據插入時必然會引發索引的變化,彙集索引沒必要說,通常都是遞增有序的。而非彙集索引就不必定是什麼數據了,其離散性致使了在插入時結構的不斷變化,從而致使插入性能下降。架構

因此爲了解決非彙集索引插入性能的問題,InnoDB引擎 創造了Insert Buffer。異步

Insert Buffer 的存儲

看到上圖,可能你們會認爲Insert Buffer 就是InnoDB 緩衝池的一個組成部分。性能

重點:其實對也不對,InnoDB 緩衝池確實包含了Insert Buffer的信息,但Insert Buffer 其實和數據頁同樣,也是物理存在的(以B+樹的形式存在共享表空間中)。優化

Insert Buffer 的做用

先說幾個點:spa

  • 一張表只能有一個主鍵索引,那是由於其物理存儲是一個B+樹。(別忘了彙集索引葉子節點存儲的數據,而數據只有一份)
  • 非彙集索引葉子節點存的是彙集索引的主鍵

彙集索引的插入

首先咱們知道在InnoDB存儲引擎中,主鍵是行惟一的標識符(也就是咱們常叨叨的彙集索引)。咱們平時插入數據通常都是按照主鍵遞增插入,所以彙集索引都是順序的,不須要磁盤的隨機讀取。線程

好比表:設計

CREATE TABLE test(
    id INT AUTO_INCREMENT,
    name VARCHAR(30),
    PRIMARY KEY(id)
);

如上我建立了一個主鍵 id,它有如下的特性:3d

  • Id列是自增加的
  • Id列插入NULL值時,因爲AUTO_INCREMENT的緣由,其值會遞增
  • 同時數據頁中的行記錄按id的值進行順序存放

通常狀況下因爲彙集索引的有序性,不須要隨機讀取頁中的數據,由於此類的順序插入速度是很是快的。

但若是你把列 Id 插入UUID這種數據,那你插入就是和非彙集索引同樣都是隨機的了。會致使你的B+ tree結構不停地變化,那性能必然會受到影響。

非彙集索引的插入

不少時候咱們的表還會有不少非彙集索引,好比我按照b字段查詢,且b字段不是惟一的。以下表:

CREATE TABLE test(
    id INT AUTO_INCREMENT,
    name VARCHAR(30),
    PRIMARY KEY(id),
    KEY(name)
);

這裏我建立了一個x表,它有如下特色:

  • 有一個彙集索引 id
  • 有一個不惟一的非彙集索引 name
  • 在插入數據時數據頁是按照主鍵id進行順序存放
  • 輔助索引 name的數據插入不是順序的

非彙集索引也是一顆B+樹,只是葉子節點存的是彙集索引的主鍵和name 的值。

由於不能保證name列的數據是順序的,因此非彙集索引這棵樹的插入必然也不是順序的了。

固然若是name列插入的是時間類型數據,那其非彙集索引的插入也是順序的。

Insert Buffer 的到來

能夠看出非彙集索引插入的離散性致使了插入性能的降低,所以InnoDB引擎設計了 Insert Buffer來提升插入性能 。

我來看看使用Insert Buffer 是怎麼插入的:

首先對於非彙集索引的插入或更新操做,不是每一次直接插入到索引頁中,而是先判斷插入的非彙集索引頁是否在緩衝池中。

若在,則直接插入;若不在,則先放入到一個Insert Buffer對象中。

給外部的感受好像是樹已經插入非彙集的索引的葉子節點,而實際上是存放在其餘位置了

以必定的頻率和狀況進行Insert Buffer和輔助索引頁子節點的merge(合併)操做,一般會將多個插入操做一塊兒進行merge,這就大大的提高了非彙集索引的插入性能。

Insert Buffer的使用要求

  • 索引是非彙集索引
  • 索引不是惟一(unique)的

只有知足上面兩個必要條件時,InnoDB存儲引擎纔會使用Insert Buffer來提升插入性能。

那爲何必須知足上面兩個條件呢?

第一點索引是非彙集索引就不用說了,人家彙集索引原本就是順序的也不須要你

第二點必須不是惟一(unique)的,由於在寫入Insert Buffer時,數據庫並不會去判斷插入記錄的惟一性。若是再去查找確定又是離散讀取的狀況了,這樣InsertBuffer就失去了意義。

Insert Buffer信息查看

咱們可使用命令SHOW ENGINE INNODB STATUS來查看Insert Buffer的信息:

-------------------------------------
INSERT BUFFER AND ADAPTIVE HASH INDEX
-------------------------------------
Ibuf: size 7545, free list len 3790, seg size 11336, 
8075308 inserts,7540969 merged sec, 2246304 merges
...

使用命令後,咱們會看到不少信息,這裏咱們只看下INSERT BUFFER 的:

  • seg size 表明當前Insert Buffer的大小 11336*16KB
  • free listlen 表明了空閒列表的長度
  • size 表明了已經合併記錄頁的數量
  • Inserts 表明了插入的記錄數
  • merged recs 表明了合併的插入記錄數量
  • merges 表明合併的次數,也就是實際讀取頁的次數

merges:merged recs大約爲1∶3,表明了Insert Buffer 將對於非彙集索引頁的離散IO邏輯請求大約下降了2/3

Insert Buffer的問題

說了這麼多針對於Insert Buffer的好處,但目前Insert Buffer也存在一個問題:

即在寫密集的狀況下,插入緩衝會佔用過多的緩衝池內存(innodb_buffer_pool),默認最大能夠佔用到1/2的緩衝池內存。

佔用了過大的緩衝池必然會對其餘緩衝池操做帶來影響

Insert Buffer的優化

MySQL5.5以前的版本中其實都叫作Insert Buffer,以後優化爲 Change Buffer 能夠看作是 Insert Buffer 的升級版。

插入緩衝( Insert Buffer)這個其實只針對 INSERT 操做作了緩衝,而Change Buffer 對INSERT、DELETE、UPDATE都進行了緩衝,因此能夠統稱爲寫緩衝,其能夠分爲:

  • Insert Buffer
  • Delete Buffer
  • Purgebuffer

總結:

Insert Buffer究竟是個什麼?

  • 其實Insert Buffer的數據結構就是一棵B+樹。
  • 在MySQL 4.1以前的版本中每張表有一棵Insert Buffer B+樹
  • 目前版本是全局只有一棵Insert Buffer B+樹,負責對全部的表的輔助索引進行Insert Buffer
  • 這棵B+樹存放在共享表空間ibdata1中

如下幾種狀況下 Insert Buffer會寫入真正非彙集索引,也就是所說的Merge Insert Buffer

  • 當輔助索引頁被讀取到緩衝池中時
  • Insert Buffer Bitmap頁追蹤到該輔助索引頁已無可用空間時
  • Master Thread線程中每秒或每10秒會進行一次Merge Insert Buffer的操做

一句話歸納下:

Insert Buffer 就是用於提高非彙集索引頁的插入性能的,其數據結構相似於數據頁的一個B+樹,物理存儲在共享表空間ibdata1中 。

image

相關文章
相關標籤/搜索