現在有這麼一個問題:當你執行一條insert語句之後,插入的數據就已經保存在磁盤中了麼?
答案是不一定 ,那是爲什麼呢?首先來了解一下MySQL在InnoDB存儲引擎中,數據是怎麼存儲的。
1. InnoDB數據存儲單元
同大多數數據庫一樣,InnoDB有頁(Page)的概念(也可以稱爲塊),頁是InnoDB磁盤管理的最小單位。在InnoDB存儲引擎中,默認每個頁的大小爲16 KB。而從InnoDB 1.2.x版本開始,可以通過參數InnoDB_page_size
將頁的大小設置爲4 K、8 K、16 K。若設置完成,則所有表中頁的大小都爲InnoDB_page_size
,不可以對其再次進行修改。除非通過mysqldump
導入和導出操作來產生新的庫。
InnoDB的數據是按數據頁爲單位來讀寫的。也就是說,當需要讀一條記錄的時候,並不是將這個記錄本身從磁盤讀出來,而是以頁爲單位,將其整體讀入內存。
而將數據從磁盤讀入內存涉及隨機IO的訪問,是數據庫裏面成本最高的操作之一,所以爲了減少磁盤IO,InnoDB設計了change buffer
這個機制。
2. change buffer
在MySQL 5.5之前的版本中,由於只支持緩存insert操作,所以最初叫做insert buffer
,只是後來的版本中支持了更多的操作類型緩存,才改叫change buffer
,這也是爲什麼代碼中有大量的ibuf前綴開頭的函數或變量。
然而使用change buffer
需要同時滿足兩個條件:
(1) 索引是輔助索引
插入聚簇索引一般是順序的,一般不需要磁盤的隨機讀取,所以不需要使用change buffer
(2) 索引不是唯一的
輔助索引不能是唯一的,因爲在插入緩衝時,數據庫並不去查找索引頁來判斷插入的記錄的唯一性。如果去查找肯定又會有離散讀取的情況發生,從而導致change buffer失去了意義。
3. change buffer的底層實現
change buffer
底層結構是一顆全局的B+樹,負責對所有的表空間進行change buffer。
4. merge buffer
從上文可知Change Buffer
是一棵B+樹。當需要實現插入記錄的輔助索引頁不在緩衝池中,輔助索引記錄首先會插入到這棵B+樹中。那麼Insert Buffer
中的記錄何時合併(merge)到真正的輔助索引中呢?
merge操作可能發生在以下幾種情況下
- 輔助索引頁被讀到緩存中
Change buffer bitmap
頁追蹤到的輔助頁已無可用空間master thread
第一種情況爲當輔助索引頁被讀取到緩衝池中時,例如這在執行正常的SELECT查詢操作,這時需要檢查Insert Buffer Bitmap
頁,然後確認該輔助索引頁是否有記錄存放於Insert Buffer
B+樹中。若有,則將Insert Buffer
B+樹中該頁的記錄插入到該輔助索引頁中。對該頁多次的記錄操作通過幾次操作合併到了原有的輔助索引頁中,因此性能會有大幅提高。
第二種情況Insert Buffer Bitmap
頁用來追蹤每個輔助索引頁的可用空間,並至少有1/32頁的空間。若插入輔助索引記錄時檢測到插入記錄後可用空間會小於1/32頁,則會強制進行一個合併操作,即強制讀取輔助索引頁,將Insert Buffer B+樹中該頁的記錄及待插入的記錄插入到輔助索引頁中。這就是上述所說的第二種情況。
第三種情況就是在Master Thread
線程中每秒或每10秒會進行一次Merge Change Buffer
的操作,不同之處在於根據線程的工作狀態每次進行merge
操作的頁的數量不同。
5. change buffer的使用場景
change buffer
主要作用是將記錄的變更緩存下來,merge的時候是真正進行數據更新的時刻,所以在一個數據頁做merge
之前,change buffer
記錄的變更越多(也就是這個頁面上要更新的次數越多),收益就越大。
因此,對於寫多讀少的業務來說,頁面在寫完以後馬上被訪問到的概率比較小,此時change buffer
的使用效果最好。這種業務模型常見的就是賬單類、日誌類的系統。