聊聊MySQL的COUNT(*)的性能
前言
基本職場(chǎng)上的程序員用來(lái)統(tǒng)計(jì)數(shù)據(jù)庫(kù)表的行數(shù)都會(huì)使用count(*),count(1)或者count(主鍵),那么它們之間的區(qū)別和性能你又是否了解呢?
其實(shí)程序員在開(kāi)發(fā)的過(guò)程中,在一張大表上統(tǒng)計(jì)總行數(shù)是非常耗時(shí)的一個(gè)操作,那么我們應(yīng)該用哪個(gè)方法統(tǒng)計(jì)會(huì)更快呢?
接下來(lái)我們就來(lái)聊一聊MySQL中統(tǒng)計(jì)總行數(shù)的方法和性能。
count(*),count(1),count(主鍵)哪個(gè)更快?
1、建表并且插入1000萬(wàn)條數(shù)據(jù)進(jìn)行實(shí)驗(yàn)測(cè)試:
# 創(chuàng)建測(cè)試表
CREATE TABLE `t6` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL,
`status` tinyint(4) NOT NULL,
PRIMARY KEY (`id`),
KEY `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
# 創(chuàng)建存儲(chǔ)過(guò)程插入1000w數(shù)據(jù)
CREATE PROCEDURE insert_1000w()
BEGIN
DECLARE i INT;
SET i=1;
WHILE i<=10000000 DO
INSERT INTO t6(name,status) VALUES('god-jiang-666',1);
SET i=i+1;
END WHILE;
END;
#調(diào)用存儲(chǔ)過(guò)程,插入1000萬(wàn)行數(shù)據(jù)
call insert_1000w();
2、分析實(shí)驗(yàn)結(jié)果
# 花了0.572秒 select count(*) from t6;

# 花了0.572秒 select count(1) from t6;

# 花了0.580秒 select count(id) from t6;

# 花了0.620秒 select count(*) from t6 force index (primary);

從上面的實(shí)驗(yàn)我們可以得出,count(*)和count(1)是最快的,其次是count(id),最慢的是count使用了強(qiáng)制主鍵的情況。
下面我們繼續(xù)測(cè)試一下它們各自的執(zhí)行計(jì)劃:
explain select count(*) from t6; show warnings;


explain select count(1) from t6; show warnings;


explain select count(id) from t6; show warnings;


explain select count(*) from t6 force index (primary); show warnings;


從上面的實(shí)驗(yàn)可以得出這三點(diǎn):
- count(*)被MySQL查詢(xún)優(yōu)化器改寫(xiě)成了count(0),并選擇了idx_status索引
- count(1)和count(id)都選擇了idx_statux索引
- 加了force index(primary)之后,走了強(qiáng)制索引
這個(gè)idx_status就是相當(dāng)于是二級(jí)輔助索引樹(shù),目的就是為了說(shuō)明: InnoDB在處理count(*)的時(shí)候,有輔助索引樹(shù)的情況下,會(huì)優(yōu)先選擇輔助索引樹(shù)來(lái)統(tǒng)計(jì)總行數(shù)。
為了驗(yàn)證count(*)會(huì)優(yōu)先選擇輔助索引樹(shù)這個(gè)結(jié)論,我們繼續(xù)來(lái)看看下面的實(shí)驗(yàn):
# 刪除idx_status索引,繼續(xù)執(zhí)行count(*) alter table t6 drop index idx_status; explain select count(*) from t6;

從以上實(shí)驗(yàn)可以得出,刪除了idx_status這個(gè)輔助索引樹(shù),count(*)就會(huì)選擇走主鍵索引。所以結(jié)論:count(*)會(huì)優(yōu)先選擇輔助索引,假如沒(méi)有輔助索引的存在,就會(huì)走主鍵索引。
為什么count(*)會(huì)優(yōu)先選擇輔助索引?
在MySQL5.7.18之前,InnoDB通過(guò)掃描聚集索引來(lái)處理count(*)語(yǔ)句。
從MySQL5.7.18開(kāi)始,InnoDB通過(guò)遍歷最小的可用二級(jí)索引來(lái)處理count(*)語(yǔ)句。如果不存在二級(jí)索引,則掃描聚集索引。
新版本為何會(huì)使用二級(jí)索引來(lái)處理count(*)呢?
因?yàn)镮nnoDB二級(jí)索引樹(shù)的葉子節(jié)點(diǎn)上存放的是主鍵,而主鍵索引樹(shù)的葉子節(jié)點(diǎn)存放的是整行數(shù)據(jù),所以二級(jí)索引樹(shù)比主鍵索引樹(shù)小。因此查詢(xún)優(yōu)化器基于成本考慮,優(yōu)先選擇的是二級(jí)索引。所以索引count(*)快于count(主鍵)。
總結(jié)
這篇文章的結(jié)論就是count(*)=count(1)>count(id)。
為什么count(id)走了主鍵索引還會(huì)更慢呢?因?yàn)閏ount(id)需要取出主鍵,然后判斷不為空,再累加,代價(jià)更高。
count(*)是會(huì)總計(jì)出所有NOT NULL和NULL的字段,而count(id)是不會(huì)統(tǒng)計(jì)NULL字段的,所以我們?cè)诮ū淼谋M量使用NOT NULL并且給它一個(gè)默認(rèn)是空即可。
最后,在以后總計(jì)數(shù)據(jù)庫(kù)表的總行數(shù)的時(shí)候,可以大膽的使用count(*)或者count(1)。
參考資料
- 《高性能MySQL》(第三版)第六章優(yōu)化COUNT()查詢(xún)
- 《MySQL實(shí)戰(zhàn)45講》林曉斌
到此這篇關(guān)于聊聊MySQL的COUNT(*)的性能的文章就介紹到這了,更多相關(guān)MySQL COUNT(*)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Mysql中count(*)、count(1)、count(主鍵id)與count(字段)的區(qū)別
- MySQL中count(*)執(zhí)行慢的解決方案
- 一文搞清楚MySQL count(*)、count(1)、count(col)區(qū)別
- MySQL?中的count(*)?與?count(1)?誰(shuí)更快一些?
- MySQL count(1)、count(*)、count(字段)的區(qū)別
- MySQL中count(*)、count(1)和count(col)的區(qū)別匯總
- MySQL中distinct和count(*)的使用方法比較
- MYSQL中統(tǒng)計(jì)查詢(xún)結(jié)果總行數(shù)的便捷方法省去count(*)
- 關(guān)于mysql innodb count(*)速度慢的解決辦法
- MySQL COUNT(*)性能原理詳解
相關(guān)文章
SQL行列轉(zhuǎn)置以及非常規(guī)的行列轉(zhuǎn)置示例代碼
轉(zhuǎn)置即旋轉(zhuǎn)數(shù)據(jù)表的橫縱方向,常用來(lái)改變數(shù)據(jù)布局,以便用新的角度觀(guān)察,下面這篇文章主要給大家介紹了關(guān)于SQL行列轉(zhuǎn)置以及非常規(guī)行列轉(zhuǎn)置的相關(guān)資料,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-08-08
淺析MySQL數(shù)據(jù)的導(dǎo)出與導(dǎo)入知識(shí)點(diǎn)
在本文里我們給大家分享了關(guān)于MySQL數(shù)據(jù)的導(dǎo)出與導(dǎo)入的相關(guān)實(shí)例和知識(shí)點(diǎn)內(nèi)容,需要的朋友們跟著學(xué)習(xí)下。2019-03-03
一文理解MySQL數(shù)據(jù)庫(kù)的約束與表的設(shè)計(jì)
約束是用來(lái)限制表中的數(shù)據(jù)長(zhǎng)什么樣子的,即什么樣的數(shù)據(jù)可以插入到表中,什么樣的數(shù)據(jù)插入不到表中,下面這篇文章主要給大家介紹了關(guān)于如何通過(guò)一文理解MySQL數(shù)據(jù)庫(kù)的約束與表的設(shè)計(jì)的相關(guān)資料,需要的朋友可以參考下2022-11-11
分享幾個(gè)簡(jiǎn)單MySQL優(yōu)化小妙招
這篇文章主要介紹了分享幾個(gè)簡(jiǎn)單MySQL優(yōu)化小妙招,分享內(nèi)容有、設(shè)置大小寫(xiě)不敏感、MySql?的用戶(hù)和權(quán)限管理等內(nèi)容,需要的小伙伴可以參考一下,需要的朋友可以參考下2022-03-03
MySQL實(shí)現(xiàn)統(tǒng)計(jì)過(guò)去12個(gè)月每個(gè)月的數(shù)據(jù)信息
這篇文章主要介紹了MySQL實(shí)現(xiàn)統(tǒng)計(jì)過(guò)去12個(gè)月每個(gè)月的數(shù)據(jù)信息,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-12-12
數(shù)據(jù)庫(kù)的用戶(hù)帳號(hào)管理基礎(chǔ)知識(shí)
數(shù)據(jù)庫(kù)的用戶(hù)帳號(hào)管理基礎(chǔ)知識(shí)...2006-11-11
MySQL里實(shí)現(xiàn)類(lèi)似SPLIT的分割字符串的函數(shù)
SQL對(duì)字符串的處理能力比較弱,比如我要循環(huán)遍歷象1,2,3,4,5這樣的字符串,如果用數(shù)組的話(huà),遍歷很簡(jiǎn)單,但是T-SQL不支持?jǐn)?shù)組,所以處理下來(lái)比較麻煩2012-09-09

