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?