前言
在實際數據庫項目開發中,因爲咱們不知道實際查詢時數據庫裏發生了什麼,也不知道數據庫是如何掃描表、如何使用索引的,所以,咱們能感知到的就只有SQL語句的執行時間。尤爲在數據規模比較大的場景下,如何寫查詢、優化查詢、如何使用索引就顯得很重要了。html
那麼,問題來了,在查詢前有沒有可能估計下查詢要掃描多少行、使用哪些索引呢?mysql
答案是確定的。以MySQL爲例,MySQL經過explain命令輸出執行計劃,對要執行的查詢進行分析。sql
什麼是執行計劃呢?數據庫
簡單來講,就是SQL在數據庫中執行時的表現狀況,一般用於SQL性能分析、優化等場景。緩存
本文從MySQL的邏輯結構講解,過渡到MySQL的查詢過程,而後給出執行計劃的例子並重點介紹執行計劃的輸出參數,從而理解爲何咱們會選擇文中建議的方案。安全
MySQL邏輯架構
MySQL邏輯架構分爲三層,以下圖。ruby
-
客戶端性能優化
- 如,鏈接處理、受權認證、安全等功能
-
核心服務服務器
- MySQL大多數核心服務均在這一層
- 包括查詢解析、分析、優化、緩存、內置函數(如,時間、數學、加密等)
- 全部的跨存儲引擎的功能也在這一層,如,存儲過程、觸發器、視圖等
-
存儲引擎數據結構
- 負責MySQL中的數據存儲和讀取
- 中間的服務層經過API與存儲引擎通訊,這些API屏蔽了不一樣存儲引擎間的差別
重點解釋下查詢緩存:對於select語句,在解析查詢以前,服務器會先檢查查詢緩存(Query Cache)。若是命中,服務器便再也不執行查詢解析、優化和執行的過程,而是直接返回緩存中的結果集。
MySQL查詢過程
若是能搞清楚MySQL是如何優化和執行查詢的,對優化查詢必定會有幫助。不少查詢優化實際上就是遵循一些原則讓優化器可以定期望的合理的方式運行。
下圖是MySQL執行一個查詢的過程。實際上每一步都比想象中的複雜,尤爲優化器,更復雜也更難理解。本文只給予簡單的介紹。
MySQL查詢過程以下:
- 客戶端將查詢發送到MySQL服務器
- 服務器先檢查查詢緩存,若是命中,當即返回緩存中的結果;不然進入下一階段
- 服務器對SQL進行解析、預處理,再由優化器生成對象的執行計劃
- MySQL根據優化器生成的執行計劃,調用存儲引擎API來執行查詢
- 服務器將結果返回給客戶端,同時緩存查詢結果
執行計劃
優化與執行
MySQL會解析查詢,並建立內部數據結構(解析樹),並對其進行各類優化,包括重寫查詢、決定表的讀取順序、選擇合適的索引等。
用戶可經過關鍵字提示(hint)優化器,從而影響優化器的決策過程。也能夠經過經過優化器解釋(explain)優化過程的各個因素,使用戶知道數據庫是如何進行優化決策的,並提供一個參考基準,便於用戶重構查詢和數據庫表的schema、修改數據庫配置等,使查詢儘量高效。
例子
看個例子。
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)
這個執行計劃給出的信息是,該查詢經過一個簡單的給定範圍的掃描,共掃描55183行,使用index condition條件在dt_user表中篩選出,掃描過程當中使用PRIMARY和idx_city_name索引。
輸出參數
輸出各字段解釋以下。更詳細的信息請參考https://dev.mysql.com/doc/refman/5.7/en/explain-output.html
-
id
- select查詢序列號
- id相同,執行順序由上至下;id不一樣,id值越大優先級越高,越先被執行
- select_type
查詢數據的操做類型,有以下
- table
顯示該行數據是關於哪張表 - partitions
匹配的分區 - type
表的鏈接類型,其值、性能由高到底排列以下
前5種狀況都是理想的索引的狀況。一般優化至少到range級別,最好能優化到ref。
- possible_keys
指出 MySQL 使用哪一個索引在該表找到行記錄。若是該值爲 NULL,說明沒有使用索引,能夠創建索引提升性能 - key
顯示 MySQL 實際使用的索引。若是爲 NULL,則沒有使用索引查詢 - key_len
表示索引中使用的字節數,經過該列計算查詢中使用的索引的長度。在不損失精確性的狀況下,長度越短越好顯示的是索引字段的最大長度,並不是實際使用長度 - ref
顯示該表的索引字段關聯了哪張表的哪一個字段 - rows
根據表統計信息及選用狀況,大體估算出找到所需的記錄或所需讀取的行數,數值越小越好 - filtered
返回結果的行數佔讀取行數的百分比,值越大越好 - extra
包含不適合在其餘列中顯示但十分重要的額外信息。常見的值以下
小結
數據庫性能優化不少,本文只簡單了介紹MySQL邏輯結構、查詢過程和執行計劃參數。根據執行計劃輸出的索引使用狀況、掃描的行數能夠預估查詢效率,幫助咱們重構查詢、優化表結構或者索引,從而儘量提供查詢效率。
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