MySQL執行計劃(explain)分析
EXPLAIN支持對SELECT、UPDATE、INSERT、REPLACE、DELETE分析
-
執行計劃能知道:
- SQL如何使用索引
- 聯接查詢的執行順序
- 查詢掃描的數據行數
-
ID列:
- 表示執行SELECT語句的順序
- ID相同時,執行順序由上至下
- ID越大優先級越高,越優先被執行
-
SELECT_TYPE列:
- SIMPLE:不包含子查詢或是UNION操作的查詢
- PRIMARY:查詢中包含任何子查詢,那麼最外層的查詢則被標記爲PRIMARY
- SUBQUERY:SELECT列表中的子查詢
- DEPENDENT SUBQUERY:依賴外部結果的子查詢
- UNION:UNION操作的第二個或是之後的查詢的值爲UNION
- DEPENDENT UNION:當UNION作爲子查詢時,第二或是第二個後的查詢的SELECT_TYPE值
- UNION RESULT:UNION產生的結果集
- DERIVED:出現在FROM子句中的子查詢
- 用途:查看查詢方法
-
TABLE列:
- 輸出數據行所在的表的名稱
- <unionM,N>由ID爲M,N查詢union產生的結果集
- <derivedN>或<subqueryN>由ID爲N的查詢產生的結果
- 用途:查看數據來源
-
PARTITIONS列
- 對於分區表,顯示查詢的分區ID
- 對於非分區表,顯示爲NULL
- 用途:用於檢查出低效率的跨分區掃描
-
TYPE列
- system:這是const聯接類型的一個特例,當查詢的表只有一行時使用
- const:表中有且只有一個匹配的行時使用,如對主鍵或是唯一索引的查詢,效率最高的聯接方式
- eq_ref: 唯一索引或主鍵查找,對於每個索引鍵,表中只有一條記錄與之匹配
- ref:非唯一索引查找,返回匹配某個單獨值的所有行。
- ref_or_null:類似於ref類型的查詢,但是附加了對NULL值列的查詢
- index_merge:該聯接類型表示使用了索引合併優化方法。
- range:索引範圍掃描,常見於between、>、<這樣的查詢條件
- index:全索引撒秒,同ALL的區別是,遍歷的是索引數
- ALL:全表掃描,效率最差的連接方式
-
EXTRA列
- distinct:優化distinct操作,在找到第一匹配的元祖後即停止找同樣值的動作
- not exists:使用Not Exists來優化查詢
- using filesort:使用額外操作進行排序,通常會出現在order by或group by查詢中
- using index:使用了覆蓋索引進行查詢
- using temporary:MySQL需要使用臨時表來處理查詢,常見於排序,子查詢,和分組查詢
- using where:需要在MySQL服務器層使用WHERE條件來過濾數據
- select tables optimized away:直接通過索引來獲取數據,不用訪問表(效率最高)
-
POSSIBLE_KEYS列
- 指出MySQL能使用哪些索引來優化查詢
- 查詢列所涉及到的列上的索引都會被列出,但不一定會被使用
-
KEY列
- 查詢優化器優化查詢實際所使用的索引
- 如果沒有可用的索引,則顯示爲NULL
- 如查詢使用了覆蓋索引,則該索引僅出現在Key列中
-
KEY_LEN列
- 表示索引字段的最大可能長度
- 長度由字段定義計算而來,並非數據的實際長度
-
REF列
- 表示哪些列或常量被用於查找索引列上的值
-
ROWS列
- 表示MySQL通過索引統計信息,估算的所需讀取的行數
- ROWS值的大小是個統計抽樣結果,並不十分準確
-
FILTERED列
- 表示返回結果的行數佔需讀取行數的百分比
- FILTERED列的值越大越好
- 依賴於統計信息
執行計劃的限制
- 無法展示存儲過程,觸發器,UDF對查詢的影響
- 無法使用EXPLAIN對存儲過程進行分析
- 早期版本的MySQL只支持對SELECT語句進行分析
常見業務優化處理
優化評論分頁查詢
例子
SELECT customer_id,title,content FROM `product_comment`
WHERE audit_status=1 AND product_id=199726
LIMIT 0,5
這裏的索引有audit_status和product_id,可以建立聯合索引。但是哪個放左邊就要計算區分度。
計算方法
SELECT COUNT(DISTINCT audit_status)/COUNT(*) AS audit_rate,
COUNT(DISTINCT product_id)/COUNT(*) AS product_rate
FROM product_comment;
區分度越高越好,放左邊。結論product_rate>audit_rate
建立聯合索引
CREATE INDEX idx_productID_auditStatus ON product_comment(product_id,audit_status)
如何刪除重複數據
業務場景:刪除評論表中對同一訂單同一商品的重複評論,只保留最早的一條。
- 查看是否存在對於同一訂單同一商品的重複評論。
SELECT order_id,product_id,COUNT(*) FROM product_comment GROUP BY order_id,product_id HAVING COUNT(*)>1;
- 備份product_comment表。
CREATE TABLE bak_product_comment_161022 LIKE product_comment;
INSERT INTO bak_product_comment_161022 SELECT * FROM product_comment;
- 刪除同一訂單的重複評論。
DELETE a
FROM product_comment a
JOIN(
SELECT order_id,product_id,MIN(comment_id) AS comment_id
FROM product_comment
GROUP BY order_id,product_id
HAVING COUNT(*)>=2
) b ON a.order_id=b.order_id AND a.product_id=b.product_id
AND a.comment_id>b.comment_id
如何進行分區間數據統計示例
業務場景:統計消費總金額大於1000元的,800到1000元的,500到800元的,以及500元以下的人數。
SELECT COUNT(CASE WHEN IFNULL(total_money,0) >=1000 THEN a.customer_id END) AS '>1000'
,COUNT(CASE WHEN IFNULL(total_money,0) >=800 AND IFNULL(total_money,0) <1000 THEN a.customer_id END) AS '800~1000'
,COUNT(CASE WHEN IFNULL(total_money,0) >=500 AND IFNULL(total_money,0) <800 THEN a.customer_id END) AS '500~800'
,COUNT(CASE WHEN IFNULL(total_money,0) <500 THEN a.customer_id END) AS '<500'
FROM mc_userdb.`customer_login` a
LEFT JOIN
( SELECT customer_id,SUM(order_money) AS total_money
FROM mc_orderdb.`order_master` GROUP BY customer_id) b
ON a.`customer_id`=b.`customer_id`
捕獲有問題的SQL-慢查日誌
- 啓動MySQL慢查日誌
set global show_query_log_file = /sql_log/show_log.log
set global log_queries_not_using_indexes = on; -- 未使用索引的SQL記錄日誌
set global long_query_time=0.001; -- 抓取執行超過多少時間的SQL(秒)
set global low_query_log=on; -- 啓動
- 如何分析慢查日誌,使用
mysqldumpslow
工具,例如:mysqldumpslow slow-mysql.log
參考
- 高性能可擴展MySQL數據庫設計及架構優化 電商項目,sqlercn,https://coding.imooc.com/class/79.html
關於我:
linxinzhe,全棧工程師,目前供職於某世界500強銀行的金融科技部門(人工智能,區塊鏈)。
GitHub:https://github.com/linxinzhe
歡迎留言討論,也歡迎關注我~
我也會關注你的哦!