InnoDB關(guān)鍵特性之insert buffer insert buffer 是InnoDB存儲引擎所獨有的功能。通過insert buffer,InnoDB存儲引擎可以大幅度提高數(shù)據(jù)庫中非唯一輔助索引的插入性能。
數(shù)據(jù)庫對于自增主鍵值的插入是順序的,因此插入能有較高的性能。但是實際生產(chǎn)環(huán)境中,用戶表中主鍵僅有并且只能有1個,然而表中可能存在多個輔助索引。
為了闡述非聚集索引寫性能問題,我們先來看一個例子:
MySQL>create table t ( id int auto_increment, name varchar(30), PRimary key (id));
我們創(chuàng)建了一個表,表的主鍵是id,id列式自增長的,即當執(zhí)行插入操作時,id列會自動增長,頁中行記錄按id順序存放,不需要隨機讀取其它頁的數(shù)據(jù)。因此,在這樣的情況下(即聚集索引),插入操作效率很高。
對于上一張表t,業(yè)務(wù)上還需要按非唯一的name字段查找,則表定義改為:
mysql>create table t ( id int auto_increment, name varchar(30), primary key (id), key (name));
這時,除了主鍵聚合索引外,還產(chǎn)生了一個name列的輔助索引,對于該非聚集索引來說,葉子節(jié)點的插入不再有序,這時就需要離散訪問非聚集索引頁,插入性能變低。
插入緩沖原理 為了解決這個問題,InnoDB設(shè)計出了插入緩沖技術(shù),對于非聚集類索引的插入和更新操作,不是每一次都直接插入到索引頁中,而是先判斷插入的非聚集索引葉子是否在緩沖池中,若在,則直接插入;若不在,則先將插入的記錄放到insert buffer中,然后根據(jù)一些算法將insert buffer 緩存的記錄通過后臺線程慢慢的合并(merge)回輔助索引頁中。這樣做的好處是:(1)減少磁盤的離散讀取;(2)將多次插入合并為一次操作。
例如name字段的插入順序為:
(‘Maria’,10), (‘David’,7), (‘Tim’, 11), (‘Jim’, 7), (‘Monty’, 10), (‘Herry’, 7), (‘Heikki’, 7)
后面的數(shù)字表示原先插入的輔助索引的page_no,可以看到頁的訪問是完全無序的,然而當插入到insert buffer中時,上述記錄可能在一個頁中,因此減少了離散讀取。在insert buffer中,記錄根據(jù)應(yīng)插入輔助索引的葉子節(jié)點page_no進行排序,故上述記錄在insert buffer中的狀態(tài)應(yīng)為:
(‘David’,7), (‘Jim’, 7), (‘Herry’, 7), (‘Heikki’, 7) , (‘Maria’,10), (‘Monty’, 10), (‘Tim’, 11)
當要進行合并時,頁page_no為7的記錄有4條,可以一次性將這4條記錄插入到輔助索引中,從而提高數(shù)據(jù)庫的整體性能。
insert buffer的使用需要滿足以下兩個條件:
(1)索引是輔助索引(secondary index)
(2)索引是非唯一的
若是唯一索引,那么在插入時需要判斷插入的記錄是否是唯一,這需要讀取輔助索引頁,而insert buffer 的設(shè)計就是避免讀取insert buffer,這會導(dǎo)致失去insert buffer 的設(shè)計意義。
插入緩沖的內(nèi)部實現(xiàn) insert buffer的數(shù)據(jù)結(jié)構(gòu)是一棵B+樹。在MySQL4.1之前的版本中每張表都有一棵insert buffer B+樹。而在現(xiàn)在的版本中,全局只有一棵insert buffer B+樹,負責對所有的表的輔助索引進行 insert buffer。這棵B+樹存放在共享表空間中,默認也就是ibdata1中。因此,試圖通過獨立表空間ibd文件恢復(fù)表中數(shù)據(jù)時,往往會導(dǎo)致check table 失敗。這是因為表的輔助索引中的數(shù)據(jù)可能還在insert buffer中,也就是共享表空間中。所以通過idb文件進行恢復(fù)后,還需要進行repair table 操作來重建表上所有的輔助索引。
insert buffer是一棵B+樹,因此其也由葉子節(jié)點和非葉子節(jié)點組成。非葉子節(jié)點存放的是查詢的search key(鍵值)。其構(gòu)造包括三個字段:space | marker | offset。
search key一共占9字節(jié),其中space占4字節(jié),marker占1字節(jié)、offset占4字節(jié)。space表示待插入記錄所在的表空間id,在InnoDB存儲引擎中,每個表有一個唯一的space id,可以通過space id查詢得知是哪張表。marker是用來兼容老版本的insert buffer。offset表示頁所在的偏移量。
當一個輔助索引需要插入到頁(space, offset)時,如果這個頁不在緩沖池中,那么InnoDB存儲引擎首先根據(jù)上述規(guī)則構(gòu)造一個search key,接下來查詢insert buffer這棵B+樹,然后再將這條記錄插入到insert buffer B+樹的葉子節(jié)點中。
對于插入到insert buffer B+樹葉子節(jié)點的記錄,需要根據(jù)如下規(guī)則進行構(gòu)造:
space | marker | offset | metadata | secondary index record
啟用insert buffer索引后,輔助索引頁(space、page_no)中的記錄可能被插入到insert buffer B+樹中,所以為了保證每次merge insert buffer頁必須成功,還需要有一個特殊的頁來標記每個輔助索引頁(space、page_no)的可用空間。這個頁的類型為insert buffer bitmap。
概括的說,merge insert buffer的操作可能發(fā)生在以下幾種情況:
(1)輔助索引頁被讀取到緩沖池時;
(2)insert buffer bitmap頁追蹤到該輔助索引頁已無可用空間時;
(3)master thread。
插入緩沖帶來的問題 插入緩沖主要帶來如下兩個壞處:
(1)可能導(dǎo)致數(shù)據(jù)庫宕機后實例恢復(fù)時間變長。如果應(yīng)用程序執(zhí)行大量的插入和更新操作,且涉及非唯一的聚集索引,一旦出現(xiàn)宕機,這時就有大量內(nèi)存中的插入緩沖區(qū)數(shù)據(jù)沒有合并至索引頁中,導(dǎo)致實例恢復(fù)時間會很長。
(2)在寫密集的情況下,插入緩沖會占用過多的緩沖池內(nèi)存(innodb_buffer_pool),默認情況下最大可以占用1/2,這在實際應(yīng)用中會帶來一定的問題。
插入緩沖的升級:change buffer InnoDB從1.0.x版本開始引入了change buffer,可以將其視為insert buffer的升級。從這個版本開始,InnoDB存儲引擎可以對DML操作——insert、delete、update都進行緩沖,它們分別是:insert buffer、delete buffer、purge buffer。
和insert buffer一樣,change buffer適用的對象依然是非唯一的輔助索引。
對一條記錄進行update操作可以分為兩個過程:
(1)將記錄標記為刪除;
(2)真正將記錄刪除。
因此delete buffer對應(yīng)update操作的第一個過程,即將記錄標記為刪除。purge buffer對應(yīng)update操作的第二個過程,即將記錄真正的刪除。同時InnoDB存儲引擎提供了參數(shù)innodb_change_buffering,用來開啟各種buffer選項。該參數(shù)的可選值為:inserts、deletes、purges、changes、all、none。inserts、deletes、purges就是前面討論過的三種情況。changes表示啟用inserts和deletes,all表示啟用所有,none表示都不啟用。
新聞熱點
疑難解答
圖片精選