MySQL · 引擎特性 · InnoDB COUNT(*) 优化(?)
在5.7版本中,InnoDB實(shí)現(xiàn)了新的handler的records接口函數(shù),當(dāng)你需要表上的精確記錄個(gè)數(shù)時(shí),會(huì)直接調(diào)用該函數(shù)進(jìn)行計(jì)算。
使用
實(shí)際上records接口函數(shù)是在優(yōu)化階段調(diào)用的,在滿足一定條件時(shí),直接去計(jì)算行級(jí)計(jì)數(shù)。其explain出來(lái)的結(jié)果相比老版本也有所不同,這里我們使用sysbench的sbtest表來(lái)進(jìn)行測(cè)試,共200萬(wàn)行數(shù)據(jù)。
mysql> show create table sbtest1\G *************************** 1. row *************************** Table: sbtest1 Create Table: CREATE TABLE `sbtest1` (`id` int(10) unsigned NOT NULL AUTO_INCREMENT,`k` int(10) unsigned NOT NULL DEFAULT '0',`c` char(120) NOT NULL DEFAULT '',`pad` char(60) NOT NULL DEFAULT '',PRIMARY KEY (`id`),KEY `k_1` (`k`) ) ENGINE=InnoDB AUTO_INCREMENT=2000001 DEFAULT CHARSET=utf8 MAX_ROWS=1000000 1 row in set (0.00 sec)mysql> explain select count(*) from sbtest1\G *************************** 1. row *************************** id: 1select_type: SIMPLE table: NULLpartitions: NULL type: NULL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: NULL filtered: NULL Extra: Select tables optimized away 1 row in set, 1 warning (0.00 sec)注意這里Extra里為”Select tables optimized away”,表示在優(yōu)化器階段已經(jīng)被優(yōu)化掉了。如果給id列帶上條件的話,則回退到之前的邏輯
mysql> explain select count(*) from sbtest1 where id > 0\G *************************** 1. row *************************** id: 1select_type: SIMPLE table: sbtest1partitions: NULL type: range possible_keys: PRIMARY key: PRIMARY key_len: 4 ref: NULL rows: 960984 filtered: 100.00 Extra: Using where; Using index 1 row in set, 1 warning (0.00 sec)實(shí)現(xiàn)
在WL#6742中,為InnoDB實(shí)現(xiàn)了handler的records函數(shù)接口
函數(shù)棧
opt_sum_query |--> get_exact_record_count|--> ha_records|--> ha_innobase::records|-->row_scan_index_for_mysql- HA_HAS_RECORDS:引擎flag,表示是否可以把count(*)下推到引擎層
- 總是使用聚集索引來(lái)進(jìn)行計(jì)算行數(shù)
- 只需要讀取主鍵值,無(wú)需去讀取外部存儲(chǔ)列(row_prebuilt_t::read_just_key),如果行記錄較大的話,就可以節(jié)省客觀的諸如內(nèi)存拷貝之類(lèi)的操作開(kāi)銷(xiāo)
- 計(jì)算過(guò)程可中斷,每檢索1000條記錄,檢查事務(wù)是否被中斷
- 由于只有一次引擎層的調(diào)用,減少了Server層和InnoDB的交互,避免了無(wú)謂的內(nèi)存操作或格式轉(zhuǎn)換
- 對(duì)于分區(qū)表,在5.7版本已經(jīng)下推到innodb層,因此分區(qū)表的計(jì)算方式(ha_innopart::records)是針對(duì)每個(gè)分區(qū)調(diào)用ha_innobase::records,再將結(jié)果累加起來(lái)
相關(guān)代碼:
commit1
commit2
缺點(diǎn)
由于總是強(qiáng)制使用聚集索引,缺點(diǎn)很明顯:當(dāng)二級(jí)索引的大小遠(yuǎn)小于聚集索引,且數(shù)據(jù)不在內(nèi)存中時(shí),使用二級(jí)索引顯然要快些,因此文件IO更少。如下例:
默認(rèn)情況下檢索所有行(以下測(cè)試都是在清空buffer pool時(shí)進(jìn)行的):
mysql> select count(*) from sbtest1; +----------+ | count(*) | +----------+ | 2000000 | +----------+ 1 row in set (3.92 sec)即時(shí)強(qiáng)制指定索引也沒(méi)用 :(
mysql> select count(*) from sbtest1 force index(k_1); +----------+ | count(*) | +----------+ | 2000000 | +----------+ 1 row in set (3.86 sec)但如果帶上一個(gè)簡(jiǎn)單的條件,讓select count(*)走索引k_1,耗費(fèi)的時(shí)間立馬下降了….
mysql> select count(*) from sbtest1 where k > 0; +----------+ | count(*) | +----------+ | 2000000 | +----------+ 1 row in set (1.05 sec)個(gè)人認(rèn)為這算是一個(gè)性能退化,退一步講,如果用戶知道force index能夠走一個(gè)更好的索引來(lái)計(jì)算行數(shù),優(yōu)化器應(yīng)該做出選擇,而不是總是無(wú)條件選擇聚集索引,提了個(gè)Bug到官方
其他
從WL#6742還提到了一個(gè)尚未公布的WL#6605,從其只言片語(yǔ)中可以推斷官方有意向?qū)崿F(xiàn)即時(shí)獲得行數(shù):
The next worklog, WL#6605, is intended to return the COUNT(*) through this handler::records() interface almost immediately in all conditions just by keeping track if the base committed count along with transaction deltas讓我們繼續(xù)對(duì)新版本保持期待吧 :)
總結(jié)
以上是生活随笔為你收集整理的MySQL · 引擎特性 · InnoDB COUNT(*) 优化(?)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: UnicodeDecodeError解决
- 下一篇: RedHat 7配置FTP服务