mysql之count問(wèn)題

相信大家都有聽(tīng)過(guò),MyISAM 計(jì)算count值是非常快的,甚至有很多文章說(shuō)MyISAM count值的計(jì)算快于InnoDb。因?yàn)镸yISAM會(huì)存儲(chǔ)count值。
的確是的,不過(guò)要注意的是,MyISAM存儲(chǔ)的是行數(shù)而不是列數(shù)【且是無(wú)條件下】,這個(gè)有什么區(qū)別呢。
看語(yǔ)句:

select count(*)  from xxx;
select count(columnName) from xxx;

相對(duì)于這2個(gè)sql,對(duì)于MyISAMInnoDb 都是第一個(gè)相對(duì)較快,MyISAM 快的原因是存儲(chǔ)了總行數(shù),
2個(gè)引擎索引列的總數(shù)時(shí),要考慮一個(gè)重要的因素便是把該列為null的排除掉,換言之,總行數(shù)100,假如count某列時(shí),設(shè)置該列可為空,正好有1行的該列沒(méi)有值,為null,那么該列的count值就是100- count(colunName==null))。這就是創(chuàng)建表結(jié)構(gòu)時(shí),把列基本都設(shè)置成not null的原因之一了。

所以不管是MyISAMInnoDb,在不要求極力求算某列count值,或者可以保證該列為not null時(shí)都可以直接計(jì)算count(*),MyISAM存儲(chǔ)了總行數(shù),而InnoDb不需要再次判斷該列是否為空的的問(wèn)題了。 so 小 count 大學(xué)問(wèn)。

那么問(wèn)題來(lái)了,假如說(shuō)count的列帶有索引,且該列not null,對(duì)于InnoDb而言是不是count列就更快了呢。
實(shí)踐得出真實(shí),來(lái)實(shí)驗(yàn)下吧。

explain SELECT count(*) from users;
explain SELECT count(name) from users;

這2個(gè)執(zhí)行計(jì)劃,其實(shí)執(zhí)行計(jì)劃都是一模一樣的,(其實(shí)從簡(jiǎn)單的執(zhí)行計(jì)劃上來(lái)看是看不出什么問(wèn)題的。)

+----+-------------+-------+------------+-------+---------------+----------+---------+------+---------+----------+-------------+
| id | select_type | table | partitions | type  | possible_keys | key      | key_len | ref  | rows    | filtered | Extra       |
+----+-------------+-------+------------+-------+---------------+----------+---------+------+---------+----------+-------------+
|  1 | SIMPLE      | users | NULL       | index | NULL          | idx_name | 767     | NULL | 2892750 |   100.00 | Using index |
+----+-------------+-------+------------+-------+---------------+----------+---------+------+---------+----------+-------------+
1 row in set, 1 warning (0.03 sec)

這是為什么呢?

簡(jiǎn)單從執(zhí)行計(jì)劃上看是行不通的,因?yàn)?code>InnoDB 在查詢(xún)count值時(shí),一般會(huì)經(jīng)歷這幾個(gè)步驟【可以看下查詢(xún)語(yǔ)句的執(zhí)行過(guò)程:一條查詢(xún)SQL如何執(zhí)行都不知道,你和咸魚(yú)有什么區(qū)別
下面將只討論InnoDB引擎。
首先計(jì)算count(*)值時(shí),選擇最小索引數(shù)進(jìn)行計(jì)算,一般都是最小二級(jí)索引【索引key最小的那棵樹(shù)】,二級(jí)索引保存的數(shù)據(jù)是主鍵,而主鍵一般不為空,所以count(*) 和 count(1) ,count(id)并沒(méi)有所謂的快慢區(qū)分,都表示返回滿(mǎn)足條件的結(jié)果集的總行數(shù)。
不過(guò)有必要在說(shuō)下count的底層說(shuō)明
1.count(1) :InnoDB 引擎遍歷整張表,但不取值。server 層對(duì)于返回的每一行,放一個(gè)數(shù)字“1”進(jìn)去,判斷是不可能為空的,按行累加。

InnoDB handles SELECT COUNT( * ) and SELECT COUNT(1) operations in the same way. There is no performance difference.
--來(lái)源MySQL文檔:https://dev.mysql.com/doc/refman/5.7/en/group-by-functions.html#function_count
----------------------華麗分割線(xiàn)-----------------
大意便是innodb以相同的方式處理count(*)和count(1),沒(méi)有性能差異。

2.count(*):返回獲取的行數(shù)的計(jì)數(shù),無(wú)論它們是否包含 NULL值。

在MySQL 5.7.18之前,InnoDB通過(guò)掃描聚集索引來(lái)處理 SELECT COUNT( * )語(yǔ)句。從MySQL 5.7.18開(kāi)始, 除非有索引或優(yōu)化器提示指示優(yōu)化器使用其他索引,否則InnoDB通過(guò)遍歷最小的可用二級(jí)索引來(lái)處理SELECT COUNT(*)語(yǔ)句。如果不存在二級(jí)索引,則將掃描聚集索引。
----mysql 文檔:https://dev.mysql.com/doc/refman/5.7/en/group-by-functions.html#function_count

3.count(id):,InnoDB 引擎會(huì)遍歷整張表,把每一行的 id 值都取出來(lái),返回給 server 層。server 層拿到 id 后,判斷是不可能為空的,就按行累加。不過(guò)count(id)相比于前2個(gè)還是稍微慢點(diǎn)的。但是程度是肉眼無(wú)法感知的。

4.count(columnName): 這個(gè)是最慢的,來(lái)看看計(jì)算這個(gè)值時(shí),InnoDB都做了些什么吧。
第一種情況:在該字段定義為not null情況下,一行行地從記錄里面讀出這個(gè)字段,判斷不能為 null,按行累加;
第二種情況:允許為null,那么在執(zhí)行時(shí),判斷到可能為null,那么需要做的就是把該值取出來(lái),判斷是否為null,不為null,進(jìn)行累加。
看到了吧,如果不定義字段為not null是會(huì)進(jìn)行賦值判斷的,是個(gè)非常糟糕的情況。

所以在使用InnoDB時(shí),計(jì)算count值時(shí)優(yōu)先順序?yàn)椋?br> 5.7 版本:count(*) = count(1) > count(id) > count(columnName)
5.7以下版本:count(*) ≈ count(1) > count(id) > count(columnName)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容