文章目錄
之前一直沒有去深入瞭解 count(1) count(id) 的區別,借這篇文章順便深入挖掘一下
- 本文章圍繞mysql explain (執行計劃)
- mysql 8.0.16 for windows
- 工具 Navicat Premium 12
- 存儲引擎 InnoDB
- 以下結果不看具體執行時間,只看執行計劃。具體的執行時間受數據量、機器性能等等因素影響就不多說了。
explain
首先需要了解一下 EXPLAIN ,他可以幫助我們優化sql
當EXPLAIN與可解釋語句一起使用時,MySQL會顯示優化程序中有關語句執行計劃的信息。也就是說,MySQL解釋了它將如何處理語句,包括有關如何連接表以及以何種順序連接的信息。
示例
新建一張表,之後的測試均是基於此表
CREATE TABLE `temp_test` (
`id` int(18) NOT NULL,
`name` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL,
`age` int(3) NULL DEFAULT NULL,
`class` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
INDEX `pri_name`(`name`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = Dynamic;
id int類型 爲主鍵
name 爲普通索引
再插入點數據
INSERT INTO temp_test VALUES
(1,'張1',1,'班級1'),
(2,'張大',2,'班級2'),
(3,'張3',3,'班級3'),
(4,'張4',4,'班級4'),
(5,'張5',5,'班級5'),
(6,'張6',6,'班級6'),
(7,'張7',1,'班級7'),
(8,'張8',8,'班級8'),
(9,'張9',9,'班級9'),
(10,'張10',10,'班級10'),
(11,'張11',11,NULL);
select * from table
讓我們看看當全表掃描時會發生什麼,執行以下sql,查看返回結果
EXPLAIN SELECT * FROM temp_test;
id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
---|---|---|---|---|---|---|---|---|---|---|---|
1 | SIMPLE | temp_test | ALL | 10 | 100.00 |
返回瞭如上表格內的結果,返回的每個字段的詳解請看下文:
- id 爲 1 意味着:這是查詢序號爲 1 的sql
- select_type 爲 SIMPLE 意味着:這是一個簡單的單表查詢沒有使用到子查詢或UNION
- table 爲 temp_test 意味着: 目標的表是剛新建的表temp_test,
- type 爲 ALL意味着: 類型是一個全表的掃描,雖然測試的數據只有10行,但是在數據量很大的情況下查詢速度會更慢,這條sql沒有使用到索引。
SELECT count(1/i)/*)
EXPLAIN SELECT count(1) FROM temp_test; 執行結果如下
id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
---|---|---|---|---|---|---|---|---|---|---|---|
1 | SIMPLE | temp_test | index | pri_name | 303 | 10 | 100.00 | Using index |
EXPLAIN SELECT count(id) FROM temp_test; 執行結果如下
id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
---|---|---|---|---|---|---|---|---|---|---|---|
1 | SIMPLE | temp_test | index | pri_name | 303 | 10 | 100.00 | Using index |
EXPLAIN SELECT count(*) FROM temp_test; 執行結果如下
id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
---|---|---|---|---|---|---|---|---|---|---|---|
1 | SIMPLE | temp_test | index | pri_name | 303 | 10 | 100.00 | Using index |
這三結果看起來差不多,與select * 相比不過是
-
type變成了 index
-
Extra 變成了 Using index
該
index
聯接類型是一樣的ALL
,只是索引樹被掃描。這有兩種方式:- 如果索引是查詢的覆蓋索引,並且可用於滿足表中所需的所有數據,則僅掃描索引樹。在這種情況下,
Extra
專欄說Using index
。僅索引掃描通常比ALL
索引的大小通常小於表數據更快 。 - 使用索引中的讀取執行全表掃描,以按索引順序查找數據行。
Uses index
沒有出現在Extra
列中。
僅使用索引樹中的信息從表中檢索列信息,而不必執行額外的搜索以讀取實際行。當查詢僅使用屬於單個索引的列時,可以使用此策略。
對於
InnoDB
具有用戶定義的聚簇索引的表,即使列中Using index
不存在 該索引,也可以使用該索引Extra
。這樣的話,如果type
是index
和key
是PRIMARY
。 - 如果索引是查詢的覆蓋索引,並且可用於滿足表中所需的所有數據,則僅掃描索引樹。在這種情況下,
簡單的來說 type變成index幾乎與全表掃描一樣。當然,這裏掃描的是索引樹,速度肯定要比ALL快點。
總結來說,這count(1/id/*/2/3) 查詢結果都差不多,分析sql執行計劃最重要的字段就是 type能達到ref 索引訪問最好。
1並不是表示第一個字段,而是表示一個固定值。 其實就可以想成表中有這麼一個字段,這個字段就是固定值1,count(1),就是計算一共有多少個1.
select 主鍵= 索引(普通) =
EXPLAIN SELECT * FROM temp_test WHERE id = 2; 執行結果如下
id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
---|---|---|---|---|---|---|---|---|---|---|---|
1 | SIMPLE | temp_test | const | PRIMARY | PRIMARY | 4 | const | 1 | 100.00 |
根據主鍵查詢,最理想的查詢
EXPLAIN SELECT * FROM temp_test WHERE NAME = '張3';執行結果如下
id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
---|---|---|---|---|---|---|---|---|---|---|---|
1 | SIMPLE | temp_test | ref | pri_name | pri_name | 303 | const | 10 | 1 |
根據索引查詢
explain輸出參數解釋
列明 | 說明 |
---|---|
id |
執行編號,標識select所屬的行。如果在語句中沒子查詢或關聯查詢,只有唯一的select,每行都將顯示1。否則,內層的select語句一般會順序編號,對應於其在原始語句中的位置 |
select_type |
顯示本行是簡單或複雜select。如果查詢有任何複雜的子查詢,則最外層標記爲PRIMARY(DERIVED、UNION、UNION RESUlT) |
table |
訪問引用哪個表 |
partitions |
匹配的分區 |
type |
數據訪問/讀取操作類型(ALL、index、range、ref、eq_ref、const/system、NULL) |
possible_keys |
揭示哪一些索引可能有利於高效的查找 |
key |
mysql決定採用哪個索引來優化查詢 |
key_len |
mysql在索引裏使用的字節數 |
ref |
顯示了之前的表在key列記錄的索引中查找值所用的列或常量 |
rows |
爲了找到所需的行而需要讀取的行數,估算值,不精確。通過把所有rows列值相乘,可粗略估算整個查詢會檢查的行數 |
filtered |
按表條件過濾的行的百分比 |
Extra |
附加信息 ,如using index、filesort等 |
id
id是用來順序標識整個查詢中SELELCT 語句的,在嵌套查詢中id越大的語句越先執行。該值可能爲NULL,如果這一行用來說明的是其他行的聯合結果。
select_type
select_type 值 |
JSON名稱 | 含義 |
---|---|---|
SIMPLE |
沒有 | 簡單SELECT (不使用 UNION 或子查詢) |
PRIMARY |
沒有 | 最 SELECT |
UNION |
沒有 | 第二次或以後的SELECT 陳述 UNION |
DEPENDENT UNION |
dependent (true ) |
a中的第二個或更晚的SELECT 語句 UNION ,取決於外部查詢 |
UNION RESULT |
union_result |
的結果UNION 。 |
SUBQUERY |
沒有 | 首先SELECT 是子查詢 |
DEPENDENT SUBQUERY |
dependent (true ) |
首先SELECT 在子查詢中,依賴於外部查詢 |
DERIVED |
沒有 | 派生表 |
DEPENDENT DERIVED |
dependent (true ) |
派生表依賴於另一個表 |
MATERIALIZED |
materialized_from_subquery |
物化子查詢 |
UNCACHEABLE SUBQUERY |
cacheable (false ) |
無法緩存結果的子查詢,必須爲外部查詢的每一行重新計算 |
UNCACHEABLE UNION |
cacheable (false ) |
UNION 屬於不可緩存的子查詢的第二個或後一個選擇(請參閱參考資料 UNCACHEABLE SUBQUERY ) |
type
type顯示的是訪問類型,是較爲重要的一個指標,結果值從好到壞依次是:
system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL ,一般來說,得保證查詢至少達到range級別,最好能達到ref。
類型 | 說明 |
---|---|
All | 最壞的情況,全表掃描 |
index | 和全表掃描一樣。只是掃描表的時候按照索引次序進行而不是行。主要優點就是避免了排序, 但是開銷仍然非常大。如在Extra列看到Using index,說明正在使用覆蓋索引,只掃描索引的數據,它比按索引次序全表掃描的開銷要小很多 |
range | 範圍掃描,一個有限制的索引掃描。key 列顯示使用了哪個索引。當使用=、 <>、>、>=、<、<=、IS NULL、<=>、BETWEEN 或者 IN 操作符,用常量比較關鍵字列時,可以使用 range |
ref | 一種索引訪問,它返回所有匹配某個單個值的行。此類索引訪問只有當使用非唯一性索引或唯一性索引非唯一性前綴時纔會發生。這個類型跟eq_ref不同的是,它用在關聯操作只使用了索引的最左前綴,或者索引不是UNIQUE和PRIMARY KEY。ref可以用於使用=或<=>操作符的帶索引的列。 |
eq_ref | 最多隻返回一條符合條件的記錄。使用唯一性索引或主鍵查找時會發生 (高效) |
const | 當確定最多隻會有一行匹配的時候,MySQL優化器會在查詢前讀取它而且只讀取一次,因此非常快。當主鍵放入where子句時,mysql把這個查詢轉爲一個常量(高效) |
system | 這是const連接類型的一種特例,表僅有一行滿足條件。 |
Null | 意味說mysql能在優化階段分解查詢語句,在執行階段甚至用不到訪問表或索引(高效) |
possible_keys
該possible_keys
列指示MySQL可以從中選擇查找此表中的行的索引。請注意,此列完全獨立於輸出中顯示的表的順序 EXPLAIN
。這意味着某些鍵possible_keys
可能無法在生成中使用生成的表順序。
如果此列是NULL
(或在JSON格式的輸出中未定義),則沒有相關索引。在這種情況下,您可以通過檢查WHERE
子句以檢查它是否引用適合索引的某些列或列來提高查詢性能。如果是,請創建適當的索引並EXPLAIN
再次檢查查詢 。請參見 第13.1.9節“ALTER TABLE語法”。
要查看錶有哪些索引,請使用。 SHOW INDEX FROM *tbl_name*
key
key列顯示MySQL實際決定使用的鍵(索引)。如果沒有選擇索引,鍵是NULL。要想強制MySQL使用或忽視possible_keys列中的索引,在查詢中使用FORCE INDEX、USE INDEX或者IGNORE INDEX。
該
key
列指示MySQL實際決定使用的密鑰(索引)。如果MySQL決定使用其中一個possible_keys
索引來查找行,那麼該索引將被列爲鍵值。可能
key
會命名值中不存在的索引possible_keys
。如果沒有possible_keys
索引適合查找行,則會發生這種情況,但查詢選擇的所有列都是其他索引的列。也就是說,命名索引覆蓋了所選列,因此雖然它不用於確定要檢索的行,但索引掃描比數據行掃描更有效。因爲
InnoDB
,即使查詢還選擇主鍵,輔助索引也可能覆蓋所選列,因爲InnoDB
主鍵值與每個輔助索引一起存儲。如果key
是NULL
,MySQL沒有找到用於更有效地執行查詢的索引。要強制MySQL使用或忽略列出的索引
possible_keys
列,使用FORCE INDEX
,USE INDEX
或IGNORE INDEX
在您的查詢。請參見第8.9.4節“索引提示”。對於
MyISAM
表,運行ANALYZE TABLE
有助於優化器選擇更好的索引。對於MyISAM
表格,myisamchk --analyze也是如此。請參見 第13.7.3.1節“ANALYZE TABLE語法”和 第7.6節“MyISAM表維護和崩潰恢復”。
key_len
key_len列顯示MySQL決定使用的鍵長度。如果鍵是NULL,則長度爲NULL。使用的索引的長度。在不損失精確性的情況下,長度越短越好 。
該
key_len
列指示MySQL決定使用的密鑰的長度。該值key_len
使您可以確定MySQL實際使用的多部分密鑰的多少部分。如果key
列說NULL
,該len_len
列也說NULL
。由於密鑰存儲格式,對於可能
NULL
比列的列,密鑰長度更大NOT NULL
。
ref
ref列顯示使用哪個列或常數與key一起從表中選擇行。
該
ref
列顯示將哪些列或常量與列中指定的索引進行比較,以key
從表中選擇行。如果值爲
func
,則使用的值是某個函數的結果。要查看哪個函數,請使用SHOW WARNINGS
以下內容EXPLAIN
查看擴展EXPLAIN
輸出。該函數實際上可能是算術運算符等運算符。
rows
該
rows
列指示MySQL認爲必須檢查以執行查詢的行數。對於
InnoDB
表格,此數字是估算值,可能並不總是準確的
filtered
該
filtered
列指示將按表條件過濾的錶行的估計百分比。最大值爲100,這意味着不會對行進行過濾。值從100開始減少表示過濾量增加。rows
顯示檢查的估計行數,rows
×filtered
表示將與下表連接的行數。例如,如果rows
是1000並且filtered
是50.00(50%),則使用下表連接的行數是1000×50%= 500。
Extra
此列包含有關MySQL如何解析查詢的其他信息。有關不同值的說明,請參閱
EXPLAIN
附加信息。沒有與該
Extra
列對應的單個JSON屬性 ; 但是,此列中可能出現的值將作爲JSON屬性公開,或作爲屬性的文本公開message
。