標籤:

MySQL偽科學之count(1)好快

閑扯

很久以前,有一次我寫了一個SQL:

select count(*) from test;

然後這個代碼被我的其中一家公司的MySQL專家看到了,叫我過去說:

你難道不知道咱們不允許寫count(*)嗎?你不知道count(1)更快嗎?

說完二話沒說把我的SQL改了。

我自然是惴惴不安。

誰對誰錯

先說結論,我是對的,count(*)不但不慢,count(1)也會和count(*)走一樣的執行計劃。

我們先看這個表:

CREATE TABLE `test` ( `id` int(11) NOT NULL AUTO_INCREMENT, `xid` int(11) DEFAULT NULL, PRIMARY KEY (`id`), KEY `xid` (`xid`)) ENGINE=InnoDB;

現在看看count(*)的執行計劃:

可以看到,這裡出現了索引覆蓋,也就是說優化器選擇了xid這個索引進行了覆蓋。

那麼看看count(1)的執行計劃:

結果是完全一樣的。其實優化器現在是很智能的,不會因為你寫了count(1)它就能出現什麼黑科技,還是老老實實的一行一行在索引上統計,但是好處是不用二次回表檢索了。

那麼如果是count某個列呢?

我們首先給這個表裡插入一條奇怪的數據:

insert into test(xid) values (null);

這個時候執行

select count(xid) from test;

結果也是很奇怪:

明顯有問題,count(*)的結果是11。

看看執行計劃:

可以看到還是索引覆蓋的,而且真的掃描了11行。

MySQL在處理這種count(columnName)的時候會自動的把為null的列忽略掉,因此帶來了看似不正確的結果。

《高性能MySQL》上關於NULL的論述中專門提到了,對於要索引的列,最好設置成not null。

這個現象也是該論調的一個有力證明。

寫給杠精

可能有人會問,如果這個表上沒有二級索引呢?豈不是count(1)更快?

其實不是這樣的,我把表的索引去掉,這是現在的表結構:

這是兩種SQL的執行計劃:

還是完全一樣。

其實想想,如果Oracle自己都發現了這個奇技淫巧,那麼他們的工程師也會對這個進行優化的。

結論

直接count(*)就好,count(1)快這個論調就是個MySQL日常迷信。

閑扯

有時候技術專家還真是水平高。

這次的題圖是獵豹,因為獵豹是最快的豹子,而不是其他的豹,選擇對的豹,才是最快的。


推薦閱讀:

Mysql高可用之Keepalived+mysql雙主
MySQL 配置二進位日誌文件
MySQL性能測試工具MySQLslap使用實例詳解
我的產品開發之旅(3) - 設計商品一級、二級分類、推薦商品表
為什麼推薦使用MySQLi?

TAG:MySQL | MySQLDBA |