天天看點

MySQL · 引擎特性 · InnoDB COUNT(*) 優化(?)

在5.7版本中,innodb實作了新的handler的records接口函數,當你需要表上的精确記錄個數時,會直接調用該函數進行計算。

實際上records接口函數是在優化階段調用的,在滿足一定條件時,直接去計算行級計數。其explain出來的結果相比老版本也有所不同,這裡我們使用sysbench的sbtest表來進行測試,共200萬行資料。

注意這裡extra裡為”select tables optimized away”,表示在優化器階段已經被優化掉了。如果給id列帶上條件的話,則回退到之前的邏輯

函數棧

ha_has_records:引擎flag,表示是否可以把count(*)下推到引擎層

總是使用聚集索引來進行計算行數

隻需要讀取主鍵值,無需去讀取外部存儲列(row_prebuilt_t::read_just_key),如果行記錄較大的話,就可以節省客觀的諸如記憶體拷貝之類的操作開銷

計算過程可中斷,每檢索1000條記錄,檢查事務是否被中斷

由于隻有一次引擎層的調用,減少了server層和innodb的互動,避免了無謂的記憶體操作或格式轉換

對于分區表,在5.7版本已經下推到innodb層,是以分區表的計算方式(ha_innopart::records)是針對每個分區調用ha_innobase::records,再将結果累加起來

相關代碼:

<a href="https://github.com/mysql/mysql-server/commit/510dd48bf510dc0a3bda9e62cede698325d05fdd" target="_blank">commit1</a>

<a href="https://github.com/mysql/mysql-server/commit/40ec5373c044547a66d5456b15d61553de8f3401" target="_blank">commit2</a>

由于總是強制使用聚集索引,缺點很明顯:當二級索引的大小遠小于聚集索引,且資料不在記憶體中時,使用二級索引顯然要快些,是以檔案io更少。如下例:

預設情況下檢索所有行(以下測試都是在清空buffer pool時進行的):

即時強制指定索引也沒用 :(

但如果帶上一個簡單的條件,讓select count(*)走索引k_1,耗費的時間立馬下降了….

讓我們繼續對新版本保持期待吧 :)

繼續閱讀