1. 程式人生 > >INNODB insert buffer 簡單分析

INNODB insert buffer 簡單分析

在mysql5.1 之前稱為Insert Buffer, 優化2級非唯一索引上插入操作的讀IO, 在5.5之後改名為Change Buffer, 功能也擴充套件為2級非唯一索引上的插入、刪除、更新、purge的讀IO優化。
change buffer的核心思想,當資料庫需要對2級快取進行修改時,先不從外存讀頁面,而是將這些更新快取在記憶體中,在特定的條件下,統一將這些更新apply到相應的2級索引頁面上,這樣做可以減少讀IO的次數,並且相鄰的頁面的讀IO可以合併。
在原始碼中的命名一直還是用ibuf,因此之後都用ibuf來指代InsertBuffer Insert Buffer的資訊可以在show innodb status中看到,例如: INSERT BUFFER AND ADAPTIVE HASH INDEX Ibuf: size 7545, free list len 3790, seg size 11336 (單位是page)
8075308 inserts, 7540969 merged recs, 2246304 merges
};
從上述的結構體可以得知,Ibuf實際上也是一棵B+樹索引,它與innodb中其他的b+樹有著完全一樣的結構。Ibuf樹中的記錄其實就是包含了記錄本身,還有記錄所在頁面號的資訊。具體下面會分析。
Ibuf本身和double wirte buffer 一樣屬於系統表空間,因此也會物化,特別是在崩潰恢復時也需要考慮在內。
Ibuf bitmap用來記錄二級非唯一索引中頁面的空閒空間的。當插入/更新會引發索引樹SMO時,Ibuf不可用,這是因為如若發生SMO,ibuf樹中記錄的頁面資訊會部分失效,而具體這些失效頁面會最終落,在哪個頁面上是未知的。因此每次對bitMap的判斷是每次ibuf插入修改時必不可少的步驟。(程式碼註釋ulint bit_offset : 根據page_no和bit計算得來,高5位是 byte_offset, 低3位是bit_offset),Ibuf bitmap也是由一系列的頁面構成,每一個Ibuf Bitmap頁面記錄了一堆索引頁面的頁面空閒空間狀況 插入操作:
實驗方法:
create table t1(id int primary key auto_increment, name varchar(2000) index idx1 name)engine=innodb;
為了防止buffer中快取二級索引頁面,因此需要事先匯入大量資料。 利用inser into select 語句匯入65536條資料,保證次級索引樹超過3層。
插入操作在btr_cur_search_to_nth_level方法中會有涉及到ibuf 的操作,索引操作在搜尋路徑時,有一把整棵B+樹的大鎖。在索引search path 方法中去調buf_get_gen 時如若快取未命中,並且是次級非唯一索引,則觸發insert buffer的發動條件
ibuf_should_try函式中剔除cluster索引和unique索引(但是可以指定忽略次級索引的unique特性)
root頁面以及非葉頁面不會用到insert buffer(根頁面常駐記憶體)
如果latchMode<=BTR_MODIFY_LEAF 即不會發生smo,才會使用insert buffer,這裡要注意的是innodb會先嚐試以樂觀的BTR_MODIFY_LEAF的方式進行,失敗了再呼叫悲觀的BTR_MODIFY_TREE去鎖整棵樹
當buf_get_gen 返回的block 為NULL時,進入ibuf_insert方法
ibuf_insert 流程:
 1. 通過buf_page_hash_get_low 檢測插入的頁面是否未在快取中命中 2. 檢測tuple size 是否過大(大於一個空頁面的freespace的 1/2) 3. 樂觀進行ibuf_insert_low(BTR_MODIFY_PREV, 不修改整棵樹) 4. 如若3失敗,則悲觀進行ibuf_insert_low(BTR_MODIFY_TREE,修改整棵樹) ibuf_insert_low操作的流程:
1. 髒讀ibuf->size, 判斷是否需要做ibuf 的contract縮小操作 根據操作型別,索引,索引記錄,spaceid, 頁面號, 計數(初始化為0xffff)等構建ibuf記錄,ibuf索引記錄的field依次為(1)spaceid (4位元組), (2)marker byte此處初始化為0 (1位元組), (3)page number (4位元組),(4)type info (4位元組,前兩位元組標示時同屬一個索引頁面的記錄計數,第三位元組標示是何種操作 插入/刪除/del by mark,第4位元組是標示記錄格式),
 (5)之後才是索引記錄的各個屬性 如果是修改整棵樹,那麼需要加上ibuf悲觀插入mutex, 再加ibuf_mutex,檢查是否ibuf有足夠空間來進行插入操作,如果沒有的話,從ibuf檔案的segment中分配一個頁面放到freelist中 4.啟動微事務mtr,此處將mtr的inside_ibuf引數設定為true 根據tuple去b+樹中做search path, 尋找在同一個索引頁面上已經快取著的插入操作,統計大小(根據ptr 想前掃到第一條屬於同一個page_no的頁面,然後再向後掃,不精確,考慮到跨多個ibuf頁面,因此是一個upperBound) 如果是刪除操作,那麼當刪除記錄後頁面為空,那麼就不需要快取這些操作 新起一個bitmap的微事務, 檢查是否index page是否合適buffered,,(如果能在buffer中的hash表中找到該頁面,或者頁面上有顯式記錄鎖,則返回失敗) 根據bitmap上的頁面空間資訊 ibuf_index_page_calc_free_from_bits,來計算merge上去的資料會不會導致索引頁面分裂。如果會分裂的話,蒐集一堆會分裂的頁面,將那些索引頁面讀到buffer中,do_merge標誌位設為true,ibuf插入返回失敗 根據spaceid 和 page_no 統計ibuf中有幾條記錄,更新資料上的count計數 修改ibuf的bitmap上的資訊(顯示bool值,該頁面上含有記錄更新的快取) 提交bitmap的微事務 樂觀地去ibuf b+樹上插入資料(認為不會分裂),如果此時頁面上只有一條記錄,預設插入一定會成功,這是為了防止一條記錄的頁面分裂 14,如果是單頁面修改的模式,如果是根頁面,那麼要更新ibuf是否為空的標誌位;如果是整棵樹的修改模式,那麼做悲觀插入(微事務需要持有cur所在頁面包括兄弟頁面的x latch)然後放掉ibuf的ibuf_pessimistic_insert_mutex, 更新ibuf的頁面統計資訊。 如果插入成功,並且不是IBUF_OP_DELETE操作的話,更新ibuf頁面上的max_trx_id. 提交ibuf微事務 插入成功後,如果ibuf過大,需要做contract 如果過程中設定了do_merge位,則做merge操作。 Ibuf 的 merge pages 操作: merge操作會有多種情況觸發,一種是innodb master執行緒主動觸發,在資料庫關閉時根據不同的引數也會merge ibuf。另一種是當有索引頁面從外存讀到記憶體,在使用前必須將ibuf中快取的內容merge過去。 master執行緒當系統io空閒時會去merge,然後每10s也會做一次,merge 系統IO能力頁面數的5%。 系統IO能力預設為200個頁面美妙。 Innodb_fast_shutdown告訴innodb在它關閉的時候該做什麼工作。有三個值可以選擇:
當選擇0時表示在innodb關閉的時候,需要purge all, merge insert buffer,flush dirty pages。這是最慢的一種關閉方式,但是restart的時候也是最快的。 merge需要拿著整個ibuf的mutex來做?不需要,因為會先讀取次級索引頁面並加次級索引頁面的latch,因此 (ibuf_merge_or_delete_for_page,這個方法是merge到索引頁面,並且刪除ibuf中記錄) Ibuf 的contract操作(merge,並且清除ibuf中的資料): ibuf_contract_for_n_page為入口函式, ibuf的b+樹呼叫btr_pcur_open_at_rnd_pos 隨機在ibuf選擇遊標來達到隨機選取頁面的目的。 之後innodb也時將選中的記錄涉及到的頁面從外存讀取到記憶體,從而真正觸發merge操作 當頁面從外存load到buffer中,會在buf_page_io_complete中呼叫ibuf_merge_or_delete_for_page保持頁面的一致性 一堆頁面檢測 開啟微事務獲取butmap頁面和bits 構造一個ibuf tuple, 去搜索某個索引頁面相關的所有ibuf快取操作,ibuf_search_tuple_build 開始一個ibuf微事務 根據pcurs指標遍歷ibuf所有同一索引頁面的操作記錄,用ibuf頁面上的max_trxid來更新索引頁面上的max_max_trxId 從ibuf tuple中提取出索引項entry 根據不同的操作型別,做 insert_to_index_page 操作, 或者是 set_del_mark操作,或者是 delete操作 刪除一條對應的ibuf記錄 設定對應的ibuf bitmap 總的來說,在系統嘗試使用insert buffer失敗(條件不滿足時)會真正去從外存讀取索引頁面,也就自然觸發了merge操作。
---------------------
原文:https://blog.csdn.net/enweitech/article/details/51690809            

前言

在前面幾期月報我們介紹了undo log、redo log以及InnoDB如何崩潰恢復來實現資料ACID的相關知識。本期我們介紹另外一種重要的資料變更日誌,也就是InnoDB change buffer。 Change buffer的主要目的是將對二級索引的資料操作快取下來,以此減少二級索引的隨機IO,並達到操作合併的效果。

在MySQL5.5之前的版本中,由於只支援快取insert操作,所以最初叫做insert buffer,只是後來的版本中支援了更多的操作型別快取,才改叫change buffer,這也是為什麼程式碼中有大量的ibuf字首開頭的函式或變數。為了表達方面,本文也將change buffer縮寫為ibuf。

由於歷史上ibuf的資料格式曾發生過多次變化,本文討論的相關內容基於如下設定:
版本為5.5及之後的版本,不涉及舊版本的邏輯,innodb_change_buffering 設定為ALL,表示快取所有操作。

ibuf btree

change buffer的物理上是一顆普通的btree,儲存在ibdata系統表空間中,根頁為ibdata的第4個page(FSP_IBUF_TREE_ROOT_PAGE_NO)。

一條ibuf 記錄大概包含如下列:
ibuf 記錄

ibuf btree通過三列(space id, page no , counter)作為主鍵來唯一決定一條記錄,其中counter是一個遞增值,目的是為了維持不同操作的有序性,例如可以通過counter來保證在merge時執行如下序列時的循序和使用者操作順序是一致的:INSERT x, DELETE-MARK x, INSERT x。

在插入ibuf記錄前我們是不知道counter的值的,因此總是先將對應tuple的counter設定為0xFFFF,然後將cursor以模式PAGE_CUR_LE定位到小於等於(space id, page no, 0xFFFF)的位置,新記錄的counter為當前位置記錄counter值加1。

ibuf btree最大預設為buffer pool size的25%,當超過25%時,可能觸發使用者執行緒同步縮減ibuf btree。為何要將ibuf btree的大小和buffer pool大小相關聯呢 ? 一個比較重要的原因是防止ibuf本身佔用過多的buffer pool資源。

ibuf bitmap

由於ibuf 快取的操作都是針對某個具體page的,因此在快取操作時必須保證該操作不會導致空page 或索引分裂。

針對第一種情況,即避免空page,主要是對purge執行緒而言,因為只有purge執行緒才會去真正的刪除二級索引上的物理記錄。在準備插入型別為IBUF_OP_DELETE的操作快取時,會預估在apply完該page上所有的ibuf entry後還剩下多少記錄(ibuf_get_volume_buffered),如果只剩下一條記錄,則拒絕本次purge操作快取,改走正常的讀入物理頁邏輯。

針對第二種情況,InnoDB通過一種特殊的page來維護每個資料頁的空閒空間大小,也就是ibuf bitmap page,該page存在於每個ibd檔案中,具有固定的page no,其檔案結構如下圖所示:

ibuf bitmap

ibuf bitmap使用4個bit來描述一個page:

  1. IBUF_BITMAP_FREE:使用2個bit來描述空閒空間大小,以16KB的page size為例,能表示的空閒空間範圍為0(0 bytes)、1(512 bytes)、2(1024 bytes)、3(2048 bytes)。很顯然,能夠快取的二級索引記錄最大不可能超過2048位元組。
    由於只有INSERT操作才可能導致page記錄滿,因此只需要對IBUF_OP_INSERT型別的操作進行判斷:

    ibuf_insert_low:

     if (op == IBUF_OP_INSERT) {
             ulint   bits = ibuf_bitmap_page_get_bits(
                     bitmap_page, page_no, zip_size, IBUF_BITMAP_FREE,
                     &bitmap_mtr);
    
             if (buffered + entry_size + page_dir_calc_reserved_space(1) > ibuf_index_page_calc_free_from_bits(zip_size, bits)) { /* Release the bitmap page latch early. */ ibuf_mtr_commit(&bitmap_mtr); /* It may not fit */ do_merge = TRUE; ibuf_get_merge_page_nos(FALSE, btr_pcur_get_rec(&pcur), &mtr, space_ids, space_versions, page_nos, &n_stored); goto fail_exit; } } 

    其中ibuf_bitmap_page_get_bits函式根據space id 和page no 獲取對應的bitmap page,找到空閒空間描述資訊;如果本次插入操作可能超出限制,則從當前cursor位置附近開始,觸發一次非同步的ibuf merge,目的是儘量將當前page的快取操作做一次合併。
    在正常的對物理頁的DML過程中,如果page內空間發生了變化,總是需要去更新對應的IBUF_BITMAP_FREE值。參考函式:btr_compressbtr_cur_optimistic_insert

  2. IBUF_BITMAP_BUFFERED:用於表示該page是否有操作快取,在ibuf_insert_low函式中,準備插入ibuf btree前設定成true。二級索引物理頁讀入記憶體時會根據該標記位判斷是否需要進行ibuf merge操作。
  3. IBUF_BITMAP_IBUF:表示該資料頁是否是ibuf btree的一部分,該標記位主要用於非同步AIO讀操作。InnoDB專門為change buffer模組分配了一個後臺AIO執行緒,如果page屬於change buffer的b樹,則使用該執行緒做非同步讀,參考函式:ibuf_page_low

操作型別

InnoDB change buffer可以對三種類型的操作進行快取:INSERT、DELETE-MARK 、DELETE操作,前兩種對應使用者執行緒操作,第三種則由purge操作觸發。

使用者可以通過引數innodb_change_buffering來控制快取何種操作:

/** Allowed values of innodb_change_buffering */
static const char* innobase_change_buffering_values[IBUF_USE_COUNT] = { "none", /* IBUF_USE_NONE */ "inserts", /* IBUF_USE_INSERT */ "deletes", /* IBUF_USE_DELETE_MARK */ "changes", /* IBUF_USE_INSERT_DELETE_MARK */ "purges", /* IBUF_USE_DELETE */ "all" /* IBUF_USE_ALL */ }; 

innodb_change_buffering預設值為all,表示快取所有操作。注意由於在二級索引上的更新操作總是先delete-mark,再insert新記錄,因此update會產生兩條ibuf entry。

快取條件

只有滿足一定條件時,操作才會被快取,所有對ibuf操作的判斷,都從btr_cur_search_to_nth_level入口,該函式用於定位到btree上滿足條件的記錄,大概的判斷條件如下:

  1. 使用者設定了選項innodb_change_buffering;
  2. 只有葉子節點才會去考慮是否使用ibuf;
  3. 對於聚集索引,不可以快取操作;
  4. 對於唯一二級索引(unique key),由於索引記錄具有唯一性,因此無法快取插入操作,但可以快取刪除操作;
  5. 表上沒有flush 操作,例如執行flush table for export時,不允許對錶進行 ibuf 快取 (通過dict_table_t::quiesce 進行標識)

參考函式:ibuf_should_try:
當滿足ibuf快取條件時,會使用兩種模式去嘗試獲取資料頁:

BUF_GET_IF_IN_POOL: 如果資料頁在記憶體中,則獲取page並返回,否則返回NULL;
BUF_GET_IF_IN_POOL_OR_WATCH:如果資料頁在記憶體中,則獲取page並返回,否則為請求的page設定一個`sentinel`(buf_pool_watch_set),相當於標記這個page,表示這個page上的記錄正在被purge。(下一小節介紹)

前者是前臺使用者執行緒觸發,後者為purge執行緒在物理清除無效資料時觸發,如果資料已經在記憶體中了,則不進行快取。隨後進入函式ibuf_insert,經過一系列的檢查後(不可產生空page 和索引分裂、未超出最大ibuf size限制)執行操作快取。

purge操作快取

對於purge操作,當page不存在於記憶體時設定的sentinel是什麼鬼?它是如何設定的,什麼時候會被清理掉,這幾個問題涉及到purge操作的快取流程:

  1. 如何設定sentinel
    當purge執行緒嘗試讀入page時,若資料頁不在buffer pool中,則呼叫函式buf_pool_watch_set,分為兩步:

    • Step1: 首先檢查page hash,如果存在於page hash中:1)若未被設定成sentinel (別的執行緒將資料頁讀入記憶體時會清理掉對應標記),返回資料頁;2)否則返回NULL;
    • Step2: 若page hash中不存在,則從buf_pool_t::watch陣列中找到一個空閒的(狀態為BUF_BLOCK_POOL_WATCH)page控制結構體物件buf_page_t,將其狀態設定為BUF_BLOCK_ZIP_PAGE,初始化相關變數,並插入到page hash中。buf_pool_t::watch陣列的大小為purge執行緒的個數,這意味著即使所有purge執行緒同時訪問同一個buffer pool instance,總會擁有一個空閒的watch陣列物件。
  2. 判斷是否可以快取purge操作
    當設定sentinel並返回後,在決定快取purge之前,需要去判斷是否有別的執行緒對同一條記錄快取了新的操作,舉個簡單的例子:

    • Step 1: delete-mark X (sec index) //session 1
    • Step 2: insert X (clust index) //session 1
    • Step 3: delete X(sec index) //purge thread
    • Step 4: insert X (sec index) //session 1

    如果二級索引頁在記憶體中,那麼Step 3 和Step4必然是有序的,因為需要獲取block鎖才能進行資料變更操作。但資料頁不在記憶體時,就需要確保Step 4在Step 3之後執行。因此在快取purge操作之前,需要根據當前要清理的記錄,找到對應的聚集索引記錄,並檢查相比當前purge執行緒的readview是否有新版本的聚集索引記錄(即有新的插入操作發生)。

    如果檢查到有新的插入,則本次purge操作直接放棄。因為當符合一定條件時,Step 4的操作可以直接把Step1產生的記錄刪除標記清除掉,重用物理空間。

    參考函式:row_purge_poss_sec
    但是注意上述檢查流程結束時,會在函式row_purge_poss_sec中將mtr提交掉,對應的聚集索引頁的Latch會被釋放掉,這意味著可能出現如下序列:

    • Step 1: delete-mark X;
    • Step 2: delete X,purge執行緒為其設定watch,並完成在函式row_purge_poss_sec中的檢查,準備插入ibuf
    • Step 3: insert X,索引頁不在記憶體,準備插入ibuf

    在函式ibuf_insert中,針對IBUF_OP_INSERT和IBUF_OP_DELETE_MARK操作,會去檢查是否對應的二級索引頁被設定成sentinel(buf_page_get_also_watch),如果是的話,表明當前有一個pending的purge操作,目前的處理邏輯是放棄insert和delete-mark的快取操作,轉而讀取物理頁。

    綜上,如果purge操作先進入ibuf_insert,則對應二級索引頁的watch必然被設定,insert操作將放棄快取,轉而嘗試讀入索引頁;如果insert先進入ibuf_insert,則purge操作的快取放棄。

    即使Purge執行緒完成一系列檢查,進入快取階段,這時候使用者執行緒依舊可能會去讀入物理頁;有沒有可能導致purge操作丟失呢 ?答案是否定的!因為purge執行緒在快取操作時先將cursor定位到ibuf btree上,對應的ibuf page已將加上latch;而使用者執行緒如果讀入物理頁,為了merge ibuf entry,也需要請求page latch;當purge執行緒在拿到latch後,會再檢查一次看看物理頁是否已讀入記憶體(buf_pool_watch_occurred),如果是的話,則放棄本次快取。

  3. 何時清理sentinel
    有兩種情況會清理sentinel:

    • 第一種情況是purge操作完成快取後(或者判斷無法進行purge快取)進行清理;
    • 第二種情況是從磁碟讀入檔案塊的時候,會呼叫buf_page_init_for_read->buf_page_init初始化一個page物件。這時候會做一個判斷,如果將被讀入的page被設定為sentinel(在watch陣列中被設定),則呼叫buf_pool_watch_remove將其從page hash中移除,對應bp->watch的資料元素被重置成空閒狀態。

ibuf merge

有以下幾種場景會觸發ibuf merge操作:

  1. 使用者執行緒選擇二級索引進行資料查詢,這時候必須要讀入二級索引頁,相應的ibuf entry需要merge到Page中。
  2. 當嘗試快取插入操作時,如果預估page的空間不足,可能導致索引分裂,則定位到嘗試快取的page no在ibuf btree中的位置,最多merge 8個(IBUF_MERGE_AREA) page,merge方式為非同步,即發起非同步讀索引頁請求。
    參考函式:ibuf_insert_low —> ibuf_get_merge_page_nos_func
  3. 若當前ibuf tree size 超過ibuf->max_size + 10(IBUF_CONTRACT_DO_NOT_INSERT)時,執行一次同步的ibuf merge(ibuf_contract),merge的page no為隨機定位的cursor,最多一次merge 8個page,同時放棄本次快取。
    其中ibuf->max_size預設為25% * buffer pool size,百分比由引數innodb_change_buffer_max_size控制,可動態調整。
    參考函式:ibuf_insert_low —> ibuf_contract

  4. 若本次插入ibuf操作可能產生ibuf btree索引分裂(BTR_MODIFY_TREE)時:
    • 當前ibuf->size < ibuf->max_size, 不做處理;
    • 當前ibuf->size >= ibuf->max_size + 5 (IBUF_CONTRACT_ON_INSERT_SYNC)時,執行一次同步ibuf merge,位置隨機;
    • 當前Ibuf->size介於ibuf->max_size 和ibuf->max_size +5 之間時。執行一次非同步ibuf merge,位置隨機。
      參考函式:ibuf_insert_low —> ibuf_contract_after_insert
  5. 後臺master執行緒發起merge
    master執行緒有三種工作狀態:
    IDLE:例項處於空閒狀態,以100%的io capacity來作merge操作:

     n_pages = PCT_IO(100);
    

    相當於一次merge的page數等於innodb_io_capacity
    參考函式:srv_master_do_idle_tasks

    ACTIVE:例項處於活躍狀態,這時候會以如下演算法計算需要merge的page數:

        /* By default we do a batch of 5% of the io_capacity */
        n_pages = PCT_IO(5);
    
        mutex_enter(&ibuf_mutex);
    
        /* If the ibuf->size is more than half the max_size then we make more agreesive contraction. +1 is to avoid division by zero. */ if (ibuf->size > ibuf->max_size / 2) { ulint diff = ibuf->size - ibuf->max_size / 2; n_pages += PCT_IO((diff * 100) / (ibuf->max_size + 1)); } mutex_exit(&ibuf_mutex); 

    可見在系統active時,會以比較溫和的方式去做merge,如果當前ibuf btree size超過最大值的一半,則嘗試多做一些merge操作。
    參考函式: srv_master_do_active_tasks

    SHUTDOWN:當執行slow shutdown時,會強制做一次全部的ibuf merge
    參考函式:srv_master_do_shutdown_tasks

  6. 對某個表執行flush table 操作時,會觸發對該表的強制ibuf merge,例如執行:

     flush table tbname for export; flush table tbname with read lock; 

    實際上強制ibuf merge主要是為flush for export準備的,當執行該命令後,為了保證能安全的將ibd拷貝到其他例項上, 需要對該表應用全部的ibuf 快取。
    參考函式:row_quiesce_table_start

“著名” bug

在change buffer的應用史上,最著名的bug要屬 bug#61104,其現象為當例項意外crash後,無法從崩潰中恢復,錯誤日誌中報如下斷言:

InnoDB: Failing assertion: page_get_n_recs(page) > 1 

最初官方花了很長的時間都沒有找到這個問題的root cause,只能加了一些程式碼邏輯避免不斷crash重啟,讓使用者有機會登入例項,重建二級索引。

後來Percona的開發人員Alexey Kopytov在bug#66819 提出了該問題的根本原因,指出ibuf entry的刪除和merge 並不是一個原子的操作(即處於兩個mtr事務中),當merge ibuf的mtr提交後crash,就可能在重啟時重複做ibuf merge。如果上次執行DELETE操作導致對應索引頁上記錄數只剩下一條,第二次apply時認為本次操作會產生空頁,從而導致斷言錯誤。

官方很快根據Alexey的意見做了修復,修復方式也比較簡單:

  1. 在第一個mtr裡,merge ibuf entry 到二級索引頁,並標記刪除ibuf entry,提交mtr;
  2. 在第二個mtr裡,執行真正的悲觀刪除ibuf entry;
  3. 在執行merge操作前,對於被delete mark的ibuf entry,不做merge操作。

具體的參考函式:ibuf_merge_or_delete_for_page 和 ibuf_delete_rec

比較烏龍的是,我們發現第一次修復並沒有處理purge執行緒產生的delete快取;我們將該發現公佈到社群,很快得到了響應,並由上游快速fix掉了,因此完整的補丁分佈在兩個版本中:

官方第一次fix(MySQL5.5.29)
官方第二次fix(MySQL5.5.31)