世界上最快的捷徑甚垦,就是腳踏實地茶鹃,本文已收錄【架構(gòu)技術(shù)專欄】關(guān)注這個喜歡分享的地方。
InnoDB引擎有幾個重點特性艰亮,為其帶來了更好的性能和可靠性:
- 插入緩沖(Insert Buffer)
- 兩次寫(Double Write)
- 自適應哈希索引(Adaptive Hash Index)
- 異步IO(Async IO)
- 刷新鄰接頁(Flush Neighbor Page)
今天我們的主題就是 插入緩沖(Insert Buffer)
,由于InnoDB引擎底層數(shù)據(jù)存儲結(jié)構(gòu)式B+樹闭翩,而對于索引我們又有聚集索引和非聚集索引。
在進行數(shù)據(jù)插入時必然會引起索引的變化迄埃,聚集索引不必說疗韵,一般都是遞增有序的。而非聚集索引就不一定是什么數(shù)據(jù)了侄非,其離散性導致了在插入時結(jié)構(gòu)的不斷變化蕉汪,從而導致插入性能降低。
所以為了解決非聚集索引插入性能的問題逞怨,InnoDB引擎 創(chuàng)造了Insert Buffer者疤。
Insert Buffer 的存儲
看到上圖,可能大家會認為Insert Buffer 就是InnoDB 緩沖池的一個組成部分叠赦。
重點:其實對也不對驹马,InnoDB 緩沖池確實包含了Insert Buffer的信息,但Insert Buffer 其實和數(shù)據(jù)頁一樣眯搭,也是物理存在的(以B+樹的形式存在共享表空間中)窥翩。
Insert Buffer 的作用
先說幾個點:
一張表只能有一個主鍵索引,那是因為其物理存儲是一個B+樹鳞仙。(別忘了聚集索引葉子節(jié)點存儲的數(shù)據(jù),而數(shù)據(jù)只有一份)
非聚集索引葉子節(jié)點存的是聚集索引的主鍵
聚集索引的插入
首先我們知道在InnoDB存儲引擎中笔时,主鍵是行唯一的標識符(也就是我們常叨叨的聚集索引)棍好。我們平時插入數(shù)據(jù)一般都是按照主鍵遞增插入,因此聚集索引都是順序的允耿,不需要磁盤的隨機讀取借笙。
比如表:
CREATE TABLE test(
id INT AUTO_INCREMENT,
name VARCHAR(30),
PRIMARY KEY(id)
);
如上我創(chuàng)建了一個主鍵 id,它有以下的特性:
- Id列是自增長的
- Id列插入NULL值時,由于AUTO_INCREMENT的原因较锡,其值會遞增
- 同時數(shù)據(jù)頁中的行記錄按id的值進行順序存放
一般情況下由于聚集索引的有序性业稼,不需要隨機讀取頁中的數(shù)據(jù),因為此類的順序插入速度是非陈煸蹋快的低散。
但如果你把列 Id 插入UUID這種數(shù)據(jù)俯邓,那你插入就是和非聚集索引一樣都是隨機的了。會導致你的B+ tree結(jié)構(gòu)不停地變化熔号,那性能必然會受到影響稽鞭。
非聚集索引的插入
很多時候我們的表還會有很多非聚集索引,比如我按照b字段查詢引镊,且b字段不是唯一的朦蕴。如下表:
CREATE TABLE test(
id INT AUTO_INCREMENT,
name VARCHAR(30),
PRIMARY KEY(id),
KEY(name)
);
這里我創(chuàng)建了一個x表,它有以下特點:
- 有一個聚集索引 id
- 有一個不唯一的非聚集索引 name
- 在插入數(shù)據(jù)時數(shù)據(jù)頁是按照主鍵id進行順序存放
- 輔助索引 name的數(shù)據(jù)插入不是順序的
非聚集索引也是一顆B+樹弟头,只是葉子節(jié)點存的是聚集索引的主鍵和name 的值吩抓。
因為不能保證name列的數(shù)據(jù)是順序的,所以非聚集索引這棵樹的插入必然也不是順序的了赴恨。
當然如果name列插入的是時間類型數(shù)據(jù)琴拧,那其非聚集索引的插入也是順序的。
Insert Buffer 的到來
可以看出非聚集索引插入的離散性導致了插入性能的下降嘱支,因此InnoDB引擎設(shè)計了 Insert Buffer來提高插入性能 蚓胸。
我來看看使用Insert Buffer 是怎么插入的:
首先對于非聚集索引的插入或更新操作,不是每一次直接插入到索引頁中除师,而是先判斷插入的非聚集索引頁是否在緩沖池中沛膳。
若在,則直接插入汛聚;若不在锹安,則先放入到一個Insert Buffer對象中。
給外部的感覺好像是樹已經(jīng)插入非聚集的索引的葉子節(jié)點倚舀,而其實是存放在其他位置了
以一定的頻率和情況進行Insert Buffer和輔助索引頁子節(jié)點的merge(合并)操作叹哭,通常會將多個插入操作一起進行merge,這就大大的提升了非聚集索引的插入性能痕貌。
Insert Buffer的使用要求:
- 索引是非聚集索引
- 索引不是唯一(unique)的
只有滿足上面兩個必要條件時风罩,InnoDB存儲引擎才會使用Insert Buffer來提高插入性能。
那為什么必須滿足上面兩個條件呢舵稠?
第一點索引是非聚集索引就不用說了超升,人家聚集索引本來就是順序的也不需要你
第二點必須不是唯一(unique)的,因為在寫入Insert Buffer時哺徊,數(shù)據(jù)庫并不會去判斷插入記錄的唯一性室琢。如果再去查找肯定又是離散讀取的情況了,這樣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 代表了已經(jīng)合并記錄頁的數(shù)量
Inserts 代表了插入的記錄數(shù)
merged recs 代表了合并的插入記錄數(shù)量
merges 代表合并的次數(shù)轿钠,也就是實際讀取頁的次數(shù)
merges:merged recs大約為1∶3巢钓,代表了Insert Buffer 將對于非聚集索引頁的離散IO邏輯請求大約降低了2/3
Insert Buffer的問題
說了這么多針對于Insert Buffer的好處病苗,但目前Insert Buffer也存在一個問題:
即在寫密集的情況下,插入緩沖會占用過多的緩沖池內(nèi)存(innodb_buffer_pool)竿报,默認最大可以占用到1/2的緩沖池內(nèi)存铅乡。
占用了過大的緩沖池必然會對其他緩沖池操作帶來影響
Insert Buffer的優(yōu)化
MySQL5.5之前的版本中其實都叫做Insert Buffer,之后優(yōu)化為 Change Buffer
可以看做是 Insert Buffer 的升級版烈菌。
插入緩沖( Insert Buffer)這個其實只針對 INSERT 操作做了緩沖阵幸,而Change Buffer 對INSERT、DELETE芽世、UPDATE都進行了緩沖挚赊,所以可以統(tǒng)稱為寫緩沖,其可以分為:
Insert Buffer
Delete Buffer
Purgebuffer
總結(jié):
Insert Buffer到底是個什么济瓢?
其實Insert Buffer的數(shù)據(jù)結(jié)構(gòu)就是一棵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 就是用于提升非聚集索引頁的插入性能的蔑鹦,其數(shù)據(jù)結(jié)構(gòu)類似于數(shù)據(jù)頁的一個B+樹,物理存儲在共享表空間ibdata1中 箕宙。