MySQL 中 count(*) 和 count(1)
一張有 100W 條資料的表
CREATETABLE`user`(
`id`int(11)unsignedNOTNULLAUTO_INCREMENT,
`username`varchar(255)DEFAULTNULL,
`address`varchar(255)DEFAULTNULL,
`password`varchar(255)DEFAULTNULL,
PRIMARYKEY(`id`)
)ENGINE=InnoDBDEFAULTCHARSET=utf8mb4;
====>
explain 分析
- type:前三個的 type 值為 index,表示全索引掃描,就是把整個索引過一遍就行(注意是索引不是整個表
- key:這個表示 MySQL 決定採用哪個索引來優化對該表的訪問,PRIMARY 表示利用主鍵索引,NULL 表示不用索引。
- key_len:這個表示 MySQL 使用的鍵長度,因為我們的主鍵型別是 INT 且非空,所以值為 4。
- Extra:這個中的 Using index 表示優化器只需要通過訪問索引就可以獲取到需要的資料(不需要回表)。
原理分析:
主鍵索引和普通索引的儲存又有所不同:
在主鍵索引中,葉子結點儲存了每一行的資料。而在普通索引中,葉子結點儲存的是主鍵值,當我們使用普通索引去搜索資料的時候,先在葉子結點中找到主鍵,再拿著主鍵去主鍵索引中查詢資料,相當於做了兩次查詢,這也就是我們平常所說的回表
對於select count(1) from user;
這個查詢來說,InnoDB 引擎會去找到一個最小的索引樹去遍歷(不一定是主鍵索引),但是不會讀取資料,而是讀到一個葉子節點,就返回 1,最後將結果累加。
對於select count(id) from user;
這個查詢來說,InnoDB 引擎會遍歷整個主鍵索引,然後讀取 id 並返回,不過因為 id 是主鍵,就在 B+ 樹的葉子節點上,所以這個過程不會涉及到隨機 IO(並不需要回表等操作去資料頁拿資料),效能也是 OK 的。
對於select count(username) from user;
select count(*) from user;
,這個 SQL 的特殊之處在於它被 MySQL 優化過,當 MySQL 看到count(*)
就知道你是想統計總記錄數,就會去找到一個最小的索引樹去遍歷,然後統計記錄數。
=========>為主鍵索引(聚集索引)的葉子節點是資料,而普通索引的葉子節點則是主鍵值,所以普通索引的索引樹要小一些。然而在上文的案例中,我們只有主鍵索引,所以最終使用的就是主鍵索引。
結論:
第一個查詢效能最高,第二個次之(因為需要讀取 id 並返回),第三個最差(因為需要全表掃描),第四個的查詢效能則接近第一個。