相信大家都有聽(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ì)于MyISAM和InnoDb 都是第一個(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的原因之一了。
所以不管是MyISAM和InnoDb,在不要求極力求算某列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)