MySQL执行计划解析
前言
在實際數(shù)據(jù)庫項目開發(fā)中,由于我們不知道實際查詢時數(shù)據(jù)庫里發(fā)生了什么,也不知道數(shù)據(jù)庫是如何掃描表、如何使用索引的,因此,我們能感知到的就只有SQL語句的執(zhí)行時間。尤其在數(shù)據(jù)規(guī)模比較大的場景下,如何寫查詢、優(yōu)化查詢、如何使用索引就顯得很重要了。
那么,問題來了,在查詢前有沒有可能估計下查詢要掃描多少行、使用哪些索引呢?
答案是肯定的。以MySQL為例,MySQL通過explain命令輸出執(zhí)行計劃,對要執(zhí)行的查詢進(jìn)行分析。
什么是執(zhí)行計劃呢?
簡單來說,就是SQL在數(shù)據(jù)庫中執(zhí)行時的表現(xiàn)情況,通常用于SQL性能分析、優(yōu)化等場景。
本文從MySQL的邏輯結(jié)構(gòu)講解,過渡到MySQL的查詢過程,然后給出執(zhí)行計劃的例子并重點介紹執(zhí)行計劃的輸出參數(shù),從而理解為什么我們會選擇文中建議的方案。
MySQL邏輯架構(gòu)
MySQL邏輯架構(gòu)分為三層,如下圖。
-
客戶端
- 如,連接處理、授權(quán)認(rèn)證、安全等功能
-
核心服務(wù)
- MySQL大多數(shù)核心服務(wù)均在這一層
- 包括查詢解析、分析、優(yōu)化、緩存、內(nèi)置函數(shù)(如,時間、數(shù)學(xué)、加密等)
- 所有的跨存儲引擎的功能也在這一層,如,存儲過程、觸發(fā)器、視圖等
-
存儲引擎
- 負(fù)責(zé)MySQL中的數(shù)據(jù)存儲和讀取
- 中間的服務(wù)層通過API與存儲引擎通信,這些API屏蔽了不同存儲引擎間的差異
重點解釋下查詢緩存:對于select語句,在解析查詢之前,服務(wù)器會先檢查查詢緩存(Query Cache)。如果命中,服務(wù)器便不再執(zhí)行查詢解析、優(yōu)化和執(zhí)行的過程,而是直接返回緩存中的結(jié)果集。
MySQL查詢過程
如果能搞清楚MySQL是如何優(yōu)化和執(zhí)行查詢的,對優(yōu)化查詢一定會有幫助。很多查詢優(yōu)化實際上就是遵循一些原則讓優(yōu)化器能夠按期望的合理的方式運(yùn)行。
下圖是MySQL執(zhí)行一個查詢的過程。實際上每一步都比想象中的復(fù)雜,尤其優(yōu)化器,更復(fù)雜也更難理解。本文只給予簡單的介紹。
MySQL查詢過程如下:
- 客戶端將查詢發(fā)送到MySQL服務(wù)器
- 服務(wù)器先檢查查詢緩存,如果命中,立即返回緩存中的結(jié)果;否則進(jìn)入下一階段
- 服務(wù)器對SQL進(jìn)行解析、預(yù)處理,再由優(yōu)化器生成對象的執(zhí)行計劃
- MySQL根據(jù)優(yōu)化器生成的執(zhí)行計劃,調(diào)用存儲引擎API來執(zhí)行查詢
- 服務(wù)器將結(jié)果返回給客戶端,同時緩存查詢結(jié)果
執(zhí)行計劃
優(yōu)化與執(zhí)行
MySQL會解析查詢,并創(chuàng)建內(nèi)部數(shù)據(jù)結(jié)構(gòu)(解析樹),并對其進(jìn)行各種優(yōu)化,包括重寫查詢、決定表的讀取順序、選擇合適的索引等。
用戶可通過關(guān)鍵字提示(hint)優(yōu)化器,從而影響優(yōu)化器的決策過程。也可以通過通過優(yōu)化器解釋(explain)優(yōu)化過程的各個因素,使用戶知道數(shù)據(jù)庫是如何進(jìn)行優(yōu)化決策的,并提供一個參考基準(zhǔn),便于用戶重構(gòu)查詢和數(shù)據(jù)庫表的schema、修改數(shù)據(jù)庫配置等,使查詢盡可能高效。
例子
看個例子。
mysql> explain select name, nickname, ctime from dt_user where city = 'shanghai' order by name; +----+-------------+------------+-------+--------------------------+---------------+---------+--------+---------+-----------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+------------+-------+--------------------------+---------------+---------+--------+---------+-----------------------+ | 1 | SIMPLE | dt_user | range | PRIMARY,idx_city_name | idx_city_name | 2945 | NULL | 55183 | Using index condition | +----+-------------+------------+-------+--------------------------+---------------+---------+--------+---------+-----------------------+ 1 row in set (0.00 sec)這個執(zhí)行計劃給出的信息是,該查詢通過一個簡單的給定范圍的掃描,共掃描55183行,使用index condition條件在dt_user表中篩選出,掃描過程中使用PRIMARY和idx_city_name索引。
輸出參數(shù)
輸出各字段解釋如下。更詳細(xì)的信息請參考https://dev.mysql.com/doc/refman/5.7/en/explain-output.html
-
id
- select查詢序列號
- id相同,執(zhí)行順序由上至下;id不同,id值越大優(yōu)先級越高,越先被執(zhí)行
- select_type
查詢數(shù)據(jù)的操作類型,有如下
- table
顯示該行數(shù)據(jù)是關(guān)于哪張表 - partitions
匹配的分區(qū) - type
表的連接類型,其值、性能由高到底排列如下
前5種情況都是理想的索引的情況。通常優(yōu)化至少到range級別,最好能優(yōu)化到ref。
- possible_keys
指出 MySQL 使用哪個索引在該表找到行記錄。如果該值為 NULL,說明沒有使用索引,可以建立索引提高性能 - key
顯示 MySQL 實際使用的索引。如果為 NULL,則沒有使用索引查詢 - key_len
表示索引中使用的字節(jié)數(shù),通過該列計算查詢中使用的索引的長度。在不損失精確性的情況下,長度越短越好顯示的是索引字段的最大長度,并非實際使用長度 - ref
顯示該表的索引字段關(guān)聯(lián)了哪張表的哪個字段 - rows
根據(jù)表統(tǒng)計信息及選用情況,大致估算出找到所需的記錄或所需讀取的行數(shù),數(shù)值越小越好 - filtered
返回結(jié)果的行數(shù)占讀取行數(shù)的百分比,值越大越好 - extra
包含不適合在其他列中顯示但十分重要的額外信息。常見的值如下
小結(jié)
數(shù)據(jù)庫性能優(yōu)化很多,本文只簡單了介紹MySQL邏輯結(jié)構(gòu)、查詢過程和執(zhí)行計劃參數(shù)。根據(jù)執(zhí)行計劃輸出的索引使用情況、掃描的行數(shù)可以預(yù)估查詢效率,幫助我們重構(gòu)查詢、優(yōu)化表結(jié)構(gòu)或者索引,從而盡可能提供查詢效率。
Reference
- 《高性能MySQL》
- https://juejin.im/post/59d83f1651882545eb54fc7e
- https://blog.csdn.net/chenshun123/article/details/79677037
- https://yq.aliyun.com/articles/599674
- https://dev.mysql.com/doc/refman/5.7/en/explain-output.html
- https://dev.mysql.com/doc/refman/5.7/en/explain-extended.html
- https://dev.mysql.com/doc/refman/5.7/en/estimating-performance.html
總結(jié)
以上是生活随笔為你收集整理的MySQL执行计划解析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 教你七步优化数据库
- 下一篇: linux cmake编译源码,linu