MySQL中count是怎样执行的?———count(1),count(id),count(非索引列),count(二级索引列)的分析
文章目錄
- 1. 前言
- 2. 建表
- 3. count是怎么樣執(zhí)行的?
- 4. count(1),count(id),count(非索引列),count(二級(jí)索引列)的分析
1. 前言
??相信在此之前,很多人都只是記憶,沒(méi)去理解,只知道count(*)、count(1)包括了所有行,在統(tǒng)計(jì)結(jié)果的時(shí)候,不會(huì)忽略列值為NULL,count(列名)只統(tǒng)計(jì)列名那一列,在統(tǒng)計(jì)結(jié)果的時(shí)候,會(huì)忽略列值為NULL的記錄。
??下面就從原理上給大家分析一下。
2. 建表
和前面一樣,用的同一個(gè)表,表中有將近10W條數(shù)據(jù)
CREATE TABLE demo_info(id INT NOT NULL auto_increment,key1 VARCHAR(100),key2 INT,key3 VARCHAR(100),key_part1 VARCHAR(100),key_part2 VARCHAR(100),key_part3 VARCHAR(100),common_field VARCHAR(100),PRIMARY KEY (id),KEY idx_key1 (key1),UNIQUE KEY uk_key2 (key2),KEY idx_key3 (key3),KEY idx_key_part(key_part1, key_part2, key_part3) )ENGINE = INNODB CHARSET=utf8mb4;3. count是怎么樣執(zhí)行的?
經(jīng)常會(huì)看到這樣的例子:
當(dāng)你需要統(tǒng)計(jì)表中有多少數(shù)據(jù)的時(shí)候,會(huì)經(jīng)常使用如下語(yǔ)句
??由于聚集索引和非聚集索引中的記錄是一一對(duì)應(yīng)的,而非聚集索引記錄中包含的列(索引列+主鍵id)是少于聚集索引(所有列)記錄的,所以同樣數(shù)量的非聚集索引記錄比聚集索引記錄占用更少的存儲(chǔ)空間。如果我們使用非聚集索引執(zhí)行上述查詢,即統(tǒng)計(jì)一下非聚集索引uk_key2中共有多少條記錄,是比直接統(tǒng)計(jì)聚集索引中的記錄數(shù)節(jié)省很多I/O成本。所以優(yōu)化器會(huì)決定使用非聚集索引uk_key2執(zhí)行上述查詢。
注意:這里已經(jīng)驗(yàn)證過(guò)了,uk_key2比其他索引成本更低。 詳情可見MySQL查詢?yōu)槭裁催x擇使用這個(gè)索引?——基于MySQL 8.0.22索引成本計(jì)算
分析一下執(zhí)行計(jì)劃
在執(zhí)行上述查詢時(shí),server層會(huì)維護(hù)一個(gè)名叫count的變量,然后:
-
server層向InnoDB要第一條記錄。
-
InnoDB找到uk_key2的第一條二級(jí)索引記錄,并返回給server層(注意:由于此時(shí)只是統(tǒng)計(jì)記錄數(shù)量,所以并不需要回表)。
-
由于count函數(shù)的參數(shù)是*,MySQL會(huì)將*當(dāng)作常數(shù)0處理。由于0并不是NULL,server層給count變量加1。
-
server層向InnoDB要下一條記錄。
-
InnoDB通過(guò)二級(jí)索引記錄的next_record屬性找到下一條二級(jí)索引記錄,并返回給server層。
-
server層繼續(xù)給count變量加1。
-
重復(fù)上述過(guò)程,直到InnoDB向server層返回沒(méi)記錄可查的消息。
-
server層將最終的count變量的值發(fā)送到客戶端。
4. count(1),count(id),count(非索引列),count(二級(jí)索引列)的分析
來(lái)看看count(1)
SELECT COUNT(1) FROM demo_info;執(zhí)行計(jì)劃和count(*)一樣
??對(duì)于count(*)、count(1)或者任意的count(常數(shù))來(lái)說(shuō),讀取哪個(gè)索引的記錄其實(shí)并不重要,因?yàn)閟erver層只關(guān)心存儲(chǔ)引擎是否讀到了記錄,而并不需要從記錄中提取指定的字段來(lái)判斷是否為NULL。所以優(yōu)化器會(huì)使用占用存儲(chǔ)空間最小的那個(gè)索引來(lái)執(zhí)行查詢。
再看一下count(id):
explain SELECT COUNT(id) FROM demo_info;??對(duì)于count(id)來(lái)說(shuō),由于id是主鍵,不論是聚集索引記錄,還是任意一個(gè)二級(jí)索引記錄中都會(huì)包含主鍵字段,所以其實(shí)讀取任意一個(gè)索引中的記錄都可以獲取到id字段,此時(shí)優(yōu)化器也會(huì)選擇占用存儲(chǔ)空間最小的那個(gè)索引來(lái)執(zhí)行查詢。
再看一下count(非索引列)
explain select count(common_field) from demo_info??對(duì)于count(非索引列)來(lái)說(shuō),優(yōu)化器選擇全表掃描,說(shuō)明只能在聚集索引的葉子結(jié)點(diǎn)順序掃描。
請(qǐng)確認(rèn)你理解了全表掃描,它是順序掃描聚集索引的所有葉子結(jié)點(diǎn)并判斷。
??而對(duì)于其他二級(jí)索引列,count(二級(jí)索引列),優(yōu)化器只能選擇包含我們指定的列的索引去執(zhí)行查詢,只能去指定非聚集索引的B+樹掃描 ,可能導(dǎo)致優(yōu)化器選擇的索引掃描代價(jià)并不是最小。
綜上所述:
??對(duì)于count(*)、count(常數(shù))、count(主鍵)形式的count函數(shù)來(lái)說(shuō),優(yōu)化器可以選擇掃描成本最小的索引執(zhí)行查詢,從而提升效率,它們的執(zhí)行過(guò)程是一樣的,只不過(guò)在判斷表達(dá)式是否為NULL時(shí)選擇不同的判斷方式,這個(gè)判斷為NULL的過(guò)程的代價(jià)可以忽略不計(jì),所以我們可以認(rèn)為count(*)、count(常數(shù))、count(主鍵)所需要的代價(jià)是相同的。
??而對(duì)于count(非索引列)來(lái)說(shuō),優(yōu)化器選擇全表掃描,說(shuō)明只能在聚集索引的葉子結(jié)點(diǎn)順序掃描。
??count(二級(jí)索引列)只能選擇包含我們指定的列的索引去執(zhí)行查詢,可能導(dǎo)致優(yōu)化器選擇的索引執(zhí)行的代價(jià)并不是最小。
??其實(shí)上述這些區(qū)別就是因?yàn)榉蔷奂饕涗洷染奂饕涗浾加酶俚拇鎯?chǔ)空間,減少更多I/O成本,所以優(yōu)化器才有了不同索引的選擇,僅此而已。
歡迎一鍵三連~
有問(wèn)題請(qǐng)留言,大家一起探討學(xué)習(xí)
----------------------Talk is cheap, show me the code-----------------------
總結(jié)
以上是生活随笔為你收集整理的MySQL中count是怎样执行的?———count(1),count(id),count(非索引列),count(二级索引列)的分析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 一句话点评国内在产主流A级车
- 下一篇: mysql distinct count