MySQL性能之count* count1 count列對(duì)比示例
正文
最近的工作中,我聽(tīng)到組內(nèi)兩名研發(fā)同學(xué)在交流數(shù)據(jù)統(tǒng)計(jì)性能的時(shí)候,聊到了以下內(nèi)容:
數(shù)據(jù)統(tǒng)計(jì)你怎么能用 count(*) 統(tǒng)計(jì)數(shù)據(jù)呢,count(*) 太慢了,要是把數(shù)據(jù)庫(kù)搞垮了那不就完了么,趕緊改用 count(1),這樣比較快......
有點(diǎn)兒好奇,難道 count(1) 的性能真的就比 count(*) 要好嗎?
印象中網(wǎng)上有很多的文章都有過(guò)類(lèi)似問(wèn)題的討論,那 MySQL 統(tǒng)計(jì)數(shù)據(jù)總數(shù) count(*) 、count(1)和count(列名) 哪個(gè)性能更優(yōu)呢?今天我們就來(lái)聊一聊這個(gè)問(wèn)題。
count() 性能與啥相關(guān)?
在討論問(wèn)題之前,我們需要先搞明白一件事:MySQL 中 count() 的性能到底與什么相關(guān)呢?
一件東西,我們知道如何取,必定需要提前知道如何存放才行,那我們可以初步判定,count() 性能應(yīng)該與存儲(chǔ)引擎相關(guān)!
我們都知道,MySQL 常見(jiàn)的存儲(chǔ)引擎有兩種:MyISAM 和 InnoDB。
在這兩種存儲(chǔ)引擎下,MySQL 對(duì)于使用 count() 返回結(jié)果的流程是不一樣的:
- **MyISAM引擎:**每張表的總行數(shù)是存儲(chǔ)在磁盤(pán)上,所以當(dāng)執(zhí)行 count() 時(shí),是直接從磁盤(pán)拿到這個(gè)值返回,能夠快速返回。
但要是在后面加了where查詢(xún)條件時(shí),統(tǒng)計(jì)總數(shù)也沒(méi)有像想象中那么快了。
- **InnoDB 引擎:**執(zhí)行 count(),需要將數(shù)據(jù)一行一行地讀,再統(tǒng)計(jì)總數(shù)。
看到這里,可能你會(huì)有這樣的疑問(wèn):
Q:為什么 InnoDB 引擎不像 MyISAM 引擎一樣,把表總記錄存儲(chǔ)起來(lái)呢?
這個(gè)問(wèn)題非常好,在回答這個(gè)問(wèn)題之前,我們先來(lái)了解一下 MVCC 是個(gè)什么東東。
MVCC 簡(jiǎn)介
所謂MVCC,全稱(chēng):Multi-Version Concurrency Control,即多版本并發(fā)控制。
MVCC 是一種并發(fā)控制的方法,一般在數(shù)據(jù)庫(kù)管理系統(tǒng)中,實(shí)現(xiàn)對(duì)數(shù)據(jù)庫(kù)的并發(fā)訪(fǎng)問(wèn),在編程語(yǔ)言中實(shí)現(xiàn)事務(wù)內(nèi)存。
MVCC 在 MySQL InnoDB 中的實(shí)現(xiàn)主要是為了提高數(shù)據(jù)庫(kù)并發(fā)性能,用更好的方式去處理讀-寫(xiě)沖突,做到即使有讀寫(xiě)沖突時(shí),也能做到不加鎖,非阻塞并發(fā)讀。

就是因?yàn)橐獙?shí)現(xiàn)多版本并發(fā)控制,所以才導(dǎo)致 InnoDB 引擎不能直接存儲(chǔ)表總記錄數(shù)。因?yàn)槊總€(gè)事務(wù)獲取到的一致性視圖都是不一樣的,所以返回的數(shù)據(jù)總記錄也是不一致的。
到這里,相信你已經(jīng)知道 InnoDB 引擎為什么不像 MyISAM 引擎一樣把表總記錄存儲(chǔ)起來(lái)了,簡(jiǎn)單理解原因就是:InnoDB 支持事務(wù),MyISAM 不支持事務(wù)。
MySQL 對(duì) count() 的優(yōu)化
我們知道了count() 性能與存儲(chǔ)引擎相關(guān),那 MySQL 在執(zhí)行 count() 操作的時(shí)候有沒(méi)有對(duì)其性能做些優(yōu)化呢?
答案是肯定有的!
InnoDB 是索引組織表,主鍵索引樹(shù)的葉子節(jié)點(diǎn)是數(shù)據(jù),而普通索引樹(shù)的葉子節(jié)點(diǎn)是主鍵值。因此,普通索引樹(shù)比主鍵索引樹(shù)小很多。對(duì)于count(*)這樣的操作,遍歷哪個(gè)索引樹(shù)得到的結(jié)果邏輯上都是一樣的。因此,MySQL優(yōu)化器會(huì)找到最小的那棵樹(shù)來(lái)遍歷。
如果你使用過(guò) show table status 命令的話(huà),就會(huì)發(fā)現(xiàn)這個(gè)命令的輸出結(jié)果里面也有一個(gè) rows 值用于顯示這個(gè)表當(dāng)前有多少行。

相信有人肯定會(huì)問(wèn),是不是這個(gè) rows 值就能代替 count() 了嗎?
其實(shí)不能,rows 這個(gè)是從從采樣估算得來(lái)的,因此它也是不是準(zhǔn)確。
官方文檔說(shuō)是在40%到50%,所以此行數(shù) rows 是不能直接使用的,如下所示:

查詢(xún)性能 PK 大起底
基于 MySQL 的 Innodb 存儲(chǔ)引擎,統(tǒng)計(jì)表的總記錄數(shù)下面這幾種做法,到底哪種效率最高?
count(主鍵id)
InnoDB引擎會(huì)遍歷整張表,把每一行的 id 值都取出來(lái),返回給 server 層。server 層拿到 id 后,判斷是不可能為空的,就按行累加。
count(1)
會(huì)統(tǒng)計(jì)表中的所有的記錄數(shù),包含字段為 null 的記錄。
同樣遍歷整張表,但不取值,server 層對(duì)返回的每一行,放一個(gè)數(shù)字1進(jìn)去,判斷是不可能為空的,按行累加。
count(字段)
分為兩種情況,字段定義為 not null 和 null:
1)為 not null 時(shí):逐行從記錄里面讀出這個(gè)字段,判斷不為 null,累加;
2)為 null 時(shí):執(zhí)行時(shí),判斷到有可能是 null,還要把值取出來(lái)再判斷一下,不是 null 才累加。
count(*)
需要注意的是,并不是帶了 * 就把所有值取出來(lái),而是 MySQL 做了專(zhuān)門(mén)的優(yōu)化,count(*) 肯定不是null,按行累加。
count(1) 和 count(*) 對(duì)比
當(dāng)表的數(shù)據(jù)量大些時(shí),對(duì)表作分析之后,使用 count(1)還要比使用 count(*)用時(shí)多了!
從執(zhí)行計(jì)劃來(lái)看, count(1) 和 count(*)的效果是一樣的。但是在表做過(guò)分析之后, count(1) 會(huì)比 count(*)的用時(shí)少些(1w以?xún)?nèi)數(shù)據(jù)量),不過(guò)差不了多少。
如果 count(1)是聚索引,那肯定是 count(1)快,但是差的很小。因?yàn)?count(*)自動(dòng)會(huì)優(yōu)化指定到那一個(gè)字段,所以沒(méi)必要去 count(1),用 count(*) sql會(huì)幫你完成優(yōu)化的,因此:count(1) 和 count(*)基本沒(méi)有差別!
總結(jié)
基于 MySQL 的 InnoDB 存儲(chǔ)引擎,統(tǒng)計(jì)表的總記錄數(shù)按照效率排序:
count(字段) < count(主鍵id) < count(1)≈count(*)
效率最高是 count(*),并不是count(1),所以建議盡量使用 count(*)。
執(zhí)行效果上:
count(*)包括了所有的列,相當(dāng)于行數(shù),在統(tǒng)計(jì)結(jié)果的時(shí)候,不會(huì)忽略列值為null
count(1)包括了忽略所有列,用1代表代碼行,在統(tǒng)計(jì)結(jié)果的時(shí)候,不會(huì)忽略列值為null
count(列名)只包括列名那一列,在統(tǒng)計(jì)結(jié)果的時(shí)候,會(huì)忽略列值為空(這里的空不是只空字符串或者0,而是表示null 的計(jì)數(shù),即某個(gè)字段值為null 時(shí),不統(tǒng)計(jì)。
執(zhí)行效率上:
- 列名為主鍵,
count(列名)會(huì)比count(1)快 - 列名不為主鍵,
count(1)會(huì)比count(列名)快 - 如果表多個(gè)列并且沒(méi)有主鍵,則
count(1)的執(zhí)行效率優(yōu)于count(*) - 如果有主鍵,則
select count(主鍵)的執(zhí)行效率是最優(yōu)的 - 如果表只有一個(gè)字段,則
select count(*)最優(yōu)。
希望今天的講解對(duì)大家有所幫助,謝謝!
更多關(guān)于MySQL count性能對(duì)比的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
SQL SERVER 2005 最小安裝經(jīng)驗(yàn)
很久以前有個(gè)疑問(wèn) 安裝SQL SERVER 2005后為什么會(huì)把VS2005給裝上了,當(dāng)時(shí)很郁悶,試想是不是在哪個(gè)環(huán)節(jié)把VS2005組件勾上的?2011-02-02
mysql配置模板(my-*.cnf)參數(shù)詳細(xì)說(shuō)明
這篇文章主要介紹了mysql配置模板就是mysql的配置文件參數(shù)說(shuō)明,需要的朋友可以參考下2015-01-01
MySQL執(zhí)行update語(yǔ)句和原數(shù)據(jù)相同會(huì)再次執(zhí)行嗎
這篇文章主要給大家介紹了關(guān)于MySQL執(zhí)行update語(yǔ)句和原數(shù)據(jù)相同是否會(huì)再次執(zhí)行的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用MySQL具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04
MySQL 兩張表數(shù)據(jù)合并的實(shí)現(xiàn)
本文主要介紹了MySQL 兩張表數(shù)據(jù)合并的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-01-01
MySQL性能優(yōu)化的一些技巧幫助你的數(shù)據(jù)庫(kù)
你完成了你的品牌新的應(yīng)用程序,一切工作就像一個(gè)魅力;突然間,一個(gè)大爆發(fā)的用戶(hù)你的MySQL服務(wù)器,您的網(wǎng)站已關(guān)閉,是什么問(wèn)題導(dǎo)致的呢?以下是MySQL性能優(yōu)化的一些技巧,將幫助你,幫助你的數(shù)據(jù)庫(kù)2013-01-01
SQL?FOREIGN?KEY約束保障表之間關(guān)系完整性關(guān)鍵規(guī)則詳解
這篇文章主要介紹了SQL?FOREIGN?KEY約束保障表之間關(guān)系完整性關(guān)鍵規(guī)則詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12

