Explain - 非官方 MySQL 8.0 優化指南 - 學習筆記
EXPLAIN
描述了 MySQL 是如何計劃,去執行給定的查詢的。即這是一個預執行的考慮,不要和查詢效能的分析混淆了。
在評估了許多可能的執行方式後,EXPLAIN 打印出了最佳的執行計劃。
在 “possible_keys” 中有可以簡明地看到其他可能選擇的計劃,但一般來說你需要在 “OPTIMIZER_TRACE” 看這些資訊。
我們推薦一直使用EXPLAIN FORMAT=JSON
,因為它會打印出查詢代價(預設格式則沒有)。查詢代價很關鍵,它讓我們可以量化地思考和交流
,曾聽到一些資料庫從業者說“建立臨時表不好”、“連表查詢不好”,你很難直接回應這些評價,因為缺少必要的上下文資訊;水在過多的情況下同樣是對人有害的。
和上面一樣,你也可以用EXPLAIN FORMAT=JSON FOR CONNECTION <connection_id>
去看一個執行中的資料庫連線是如何最佳化執行的。這在診斷偶現的錯誤時是很有用的,因為資料和底層引數的修改會影響執行計劃的選擇。
例子1:查詢亞洲中人口多於 500W 的國家,一次全表掃描
EXPLAIN FORMAT=JSON SELECT * FROM Country WHERE continent='Asia' and population > 5000000;
{ "query_block": { "select_id": 1, "cost_info": { "query_cost": "53.80"# 這次查詢的代價 }, "table": { "table_name": "Country", "access_type": "ALL",# ALL 即全表掃描 "rows_examined_per_scan": 239,# 訪問了全表 239 行 "rows_produced_per_join": 11, "filtered": "4.76", "cost_info": { "read_cost": "51.52", "eval_cost": "2.28", "prefix_cost": "53.80", "data_read_per_join": "2K" }, "used_columns": [ ... ], "attached_condition": "((`world`.`Country`.`Continent` = 'Asia') and (`world`.`Country`.`Population` > 5000000))" } } }
例子1 中EXPLAIN 表明了這次查詢會執行全表掃描,需要花費 53.80 的代價。資訊表明沒有可用的索引possible_keys
(這一項通常就在access_type
後面)。
rows_examined_per_scan
這個數字 239 是全表掃描的行數,近似全世界國家的總數,需要指出這個數字不會是百分之百精確的,因為那樣會影響 EXPLAIN 的效能。
attached_condition
附加狀態這一項表明在讀取每一行時,會用到過濾器,條件就是“在亞洲中、人口多於 500W”。在有索引的情況下,這些條件在讀取行之前就可以判斷了,但條件被列出來就說明不是這種情況。
我們來對比一下 掃描過的行數(239) 和 返回結果的行數(32),傳送給客戶端的結果行在執行完查詢才能得知。這次查詢中服務端掃描了是返回結果 7.5倍 的行數,表明是可以優化的。
非常小的比例(甚至 1:1)並不意味著不能優化。要追求更好,可以再優化分割槽或分頁大小。
需要指出,掃描 239 行不算多,而且所有行都可以放進記憶體。但全表掃描還是很消耗記憶體的 ,而隨著表的增大效能會下降得越來越快。新增索引可以改善效能,並在資料增長時維持這種效能。