神奇的 SQL 之 MySQL 性能分析神器 →EXPLAIN,SQL 起飛的基石

前言

某一天,樓主打完上班卡,坐在工位逛園子的時候,右下角的 QQ 閃了起來,並且仍是個美女頭像!我又驚又喜,腦中閃過我所認識的可能聯繫個人女性,得出個結論:她們這會不可能聯繫我呀,圖像也沒映象,究竟是誰了?打開聊天窗口聊了起來mysql

  她:您好,我是公司客服某某某,請問 xxx後臺 是您負責的嗎?android

  我:您好,是我負責的,有什麼問題嗎?ios

  她:我發現 xxx 頁面點查詢後,一直是 加載中... ,數據一直出不來,能幫忙看看嗎?sql

  我:是否是您的姿式不對?緩存

  她:我就 xxx,而後點查詢elasticsearch

  我:騷等下,我試試,確實有點慢,很長時間才能出來ide

  她:是的,太慢了,出不來,都急死我了,能快點嗎?性能

  我:確定能、必須能!您以爲什麼速度讓您以爲最舒服?優化

  她:越快越好吧spa

  我:呃...,是嗎,我先看看是什麼問題,處理好了告訴您,保證讓您以爲舒服!

  她:好的,謝謝!

公司沒有專門的搜索服務,都是直接從 MySQL 查詢,作簡單的數據處理後返回給頁面,慢的緣由確定就是 SQL 查詢了。找到對應的查詢 SQL ,就是兩個表的聯表查詢,鏈接鍵也有索引,WHERE 條件也能走索引,怎麼會慢了?而後我用 EXPLAIN 看了下這條 SQL 的執行計劃,找到了慢的緣由,具體緣由後面揭曉(誰讓你不是豬腳!)

EXPLAIN 是什麼

它是 MySQL 的一個命令,用來查看 SQL 的執行計劃(SQL 如何執行),根據其輸出結果,咱們可以知道如下信息:表的讀取順序,數據讀取類型,哪些索引可使用,哪些索引實際使用了,表之間的鏈接類型,每張表有多少行被優化器查詢等信息,根據這些信息,咱們能夠找出 SQL 慢的緣由,並作針對性的優化

MySQL 5.6 以前的版本,EXPLAIN 只能用於查看 SELECT 的執行計劃,而從 MySQL 5.6 開始,能夠查看 SELECT 、 DELETE 、 INSERT 、 REPLACE 和 UPDATE 的執行計劃,這可不是我瞎掰,不信的能夠去 MySQL 的官網查看:Understanding the Query Execution Plan

EXPLAIN 使用方式很是簡單,簡單的你都不敢相信,就是在咱們常寫的 SELECT 、 DELETE 、 INSERT 、 REPLACE 和 UPDATE 語句以前加上 EXPLAIN 便可

EXPLAIN SELECT * FROM mysql.`user`;

EXPLAIN DELETE FROM t_user WHERE user_name = '123';

莫看 EXPLAIN 短,但它胖呀

雖然 EXPLAIN 使用起來很是簡單,但它的輸出結果中信息量很是大,雖然我胖,但我肚中有貨呀!

環境和數據準備

MySQL 版本是 5.7.2 ,存儲引擎是 InnoDB

-- 查看 MySQL 版本
SELECT VERSION();

-- MySQL 提供什麼存儲引擎
SHOW ENGINES;

-- 查看默認存儲引擎
SHOW VARIABLES LIKE '%storage_engine%';

神奇的 SQL 之 MySQL 性能分析神器 →EXPLAIN,SQL 起飛的基石


準備兩張表:用戶表 tbl_user 和用戶登陸記錄表 tbl_user_login_log ,並初始化部分部分數據

-- 表建立與數據初始化
DROP TABLE IF EXISTS tbl_user;
CREATE TABLE tbl_user (
 id INT(11) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增主鍵',
 user_name VARCHAR(50) NOT NULL COMMENT '用戶名',
 sex TINYINT(1) NOT NULL COMMENT '性別, 1:男,0:女',
 create_time datetime NOT NULL COMMENT '建立時間',
 update_time datetime NOT NULL COMMENT '更新時間',
 remark VARCHAR(255) NOT NULL DEFAULT '' COMMENT '備註',
 PRIMARY KEY (id)
) COMMENT='用戶表';

DROP TABLE IF EXISTS tbl_user_login_log;
CREATE TABLE tbl_user_login_log (
 id INT(11) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增主鍵',
 user_name VARCHAR(50) NOT NULL COMMENT '用戶名',
 ip VARCHAR(15) NOT NULL COMMENT '登陸IP',
 client TINYINT(1) NOT NULL COMMENT '登陸端, 1:android, 2:ios, 3:PC, 4:H5',
 create_time datetime NOT NULL COMMENT '建立時間',
 PRIMARY KEY (id)
) COMMENT='登陸日誌';
INSERT INTO tbl_user(user_name,sex,create_time,update_time,remark) VALUES
('何天香',1,NOW(), NOW(),'朗眉星目,一表人材'),
('薛沉香',0,NOW(), NOW(),'天星樓的總樓主薛搖紅的女兒,也是天星樓的少總樓主,體態豐盈,烏髮飄逸,指若春蔥,袖臂如玉,風姿卓然,高貴典雅,人稱「天星絕香」的武林第一大美女'),
('慕容蘭娟',0,NOW(), NOW(),'武林東南西北四大世家之北世家慕容長明的獨生女兒,生得玲瓏剔透,粉雕玉琢,脾氣倒是剛烈無比,又喜着火紅,因此人送綽號「火鳳凰」,是除天星樓薛沉香以外的武林第二大美女'),
('萇婷',0,NOW(), NOW(),'當今皇上最寵愛的侄女,北王府的郡主,腰肢纖細,遍體羅綺,眉若墨畫,脣點櫻紅;雖無沉香之雅重,蘭娟之熱烈,卻別現出一種空靈'),
('柳含姻',0,NOW(), NOW(),'武林四絕之一的添愁仙子董婉婉的徒弟,體態窈窕,姿容秀麗,真個是秋水爲神玉爲骨,芙蓉如面柳如腰,眉若墨畫,脣若點櫻,不弱西子半分,更勝玉環一籌; 搖紅樓、聽雨軒,琵琶一曲值千金!'),
('李凝雪',0,NOW(), NOW(),'李相國的女兒,神采奕奕,英姿颯爽,愛憎分明'),
('周遺夢',0,NOW(), NOW(),'音神傳人,湘妃竹琴的擁有者,雲髻高盤,穿了一身黑色蟬翼紗衫,愈以爲冰肌玉骨,粉面櫻脣,格外嬌豔動人'),
('葉留痕',0,NOW(), NOW(),'聖域聖女,膚白如雪,白衣飄飄,宛如仙女通常,微笑中帶着說不出的柔和之美'),
('郭疏影',0,NOW(), NOW(),'揚灰右使的徒弟,秀髮細眉,玉肌豐滑,嬌潤脫俗'),
('鍾鈞天',0,NOW(), NOW(),'天界,玄天九部 - 鈞天部的部主,超凡脫俗,仙氣逼人'),
('王雁雲',0,NOW(), NOW(),'塵緣山莊二小姐,刁蠻任性'),
('許侍霜',0,NOW(), NOW(),'藥王谷谷主女兒,醫術高明'),
('馮黯凝',0,NOW(), NOW(),'桃花門門主,嬌豔如火,千嬌百媚');
INSERT INTO tbl_user_login_log(user_name, ip, client, create_time) VALUES
('薛沉香', '10.53.56.78',2, '2019-10-12 12:23:45'),
('萇婷', '10.53.56.78',2, '2019-10-12 22:23:45'),
('慕容蘭娟', '10.53.56.12',1, '2018-08-12 22:23:45'),
('何天香', '10.53.56.12',1, '2019-10-19 10:23:45'),
('柳含姻', '198.11.132.198',2, '2018-05-12 22:23:45'),
('馮黯凝', '198.11.132.198',2, '2018-11-11 22:23:45'),
('周遺夢', '198.11.132.198',2, '2019-06-18 22:23:45'),
('郭疏影', '220.181.38.148',3, '2019-10-21 09:45:56'),
('薛沉香', '220.181.38.148',3, '2019-10-26 22:23:45'),
('萇婷', '104.69.160.60',4, '2019-10-12 10:23:45'),
('王雁雲', '104.69.160.61',4, '2019-10-16 20:23:45'),
('李凝雪', '104.69.160.62',4, '2019-10-17 20:23:45'),
('許侍霜', '104.69.160.63',4, '2019-10-18 20:23:45'),
('葉留痕', '104.69.160.64',4, '2019-10-19 20:23:45'),
('王雁雲', '104.69.160.65',4, '2019-10-20 20:23:45'),
('葉留痕', '104.69.160.66',4, '2019-10-21 20:23:45');

SELECT * FROM tbl_user;
SELECT * FROM tbl_user_login_log;

EXPLAIN 輸出格式概覽

咱們先來看看 EXPLAIN 輸出結果的大概,是否是長得滿臉麻子,讓咱們望而生畏 ?

神奇的 SQL 之 MySQL 性能分析神器 →EXPLAIN,SQL 起飛的基石


白白淨淨的,挺好,關鍵長啊! 官方解釋以下

神奇的 SQL 之 MySQL 性能分析神器 →EXPLAIN,SQL 起飛的基石


EXPLAIN 輸出格式詳解

EXPLAIN 的輸出字段雖然有點多,但常關注的就那麼幾個,但樓主秉着負責的態度,都給你們講一下,須要重點關注的字段,樓主也會標明滴

EXPLAIN 支持的 SQL 語句有好幾種,但工做中用的最多的仍是 SELECT ,因此樓主就偷個懶,以 SELECT 來說解 EXPLAIN,有興趣的老爺去試試其餘的

id

輸出的是整數,用來標識整個 SQL 的執行順序。id 若是相同,從上往下依次執行id不一樣;id 值越大,執行優先級越高,越先被執行;若是行引用其餘行的並集結果,則該值能夠爲NULL

神奇的 SQL 之 MySQL 性能分析神器 →EXPLAIN,SQL 起飛的基石


不重要,有所瞭解就好(其實很是簡單,看一遍基本就能記住了)

select_type

查詢的類型,官方說明以下

神奇的 SQL 之 MySQL 性能分析神器 →EXPLAIN,SQL 起飛的基石


簡單幫你們翻譯一下(有能力的去讀官網,畢竟那是原配,最具權威性)

SIMPLE:簡單的 SELECT 查詢,沒有 UNION 或者子查詢,包括單表查詢或者多表 JOIN 查詢

神奇的 SQL 之 MySQL 性能分析神器 →EXPLAIN,SQL 起飛的基石


神奇的 SQL 之 MySQL 性能分析神器 →EXPLAIN,SQL 起飛的基石


PRIMARY: 最外層的 select 查詢,常見於子查詢或 UNION 查詢 ,最外層的查詢被標識爲 PRIMARY

神奇的 SQL 之 MySQL 性能分析神器 →EXPLAIN,SQL 起飛的基石


UNION:UNION 操做的第二個或以後的 SELECT,不依賴於外部查詢的結果集(外部查詢指的就是 PRIMARY 對應的 SELECT)

神奇的 SQL 之 MySQL 性能分析神器 →EXPLAIN,SQL 起飛的基石


神奇的 SQL 之 MySQL 性能分析神器 →EXPLAIN,SQL 起飛的基石


DEPENDENT UNION:UNION 操做的第二個或以後的 SELECT,依賴於外部查詢的結果集

神奇的 SQL 之 MySQL 性能分析神器 →EXPLAIN,SQL 起飛的基石


UNION RESULT:UNION 的結果(若是是 UNION ALL 則無此結果)

神奇的 SQL 之 MySQL 性能分析神器 →EXPLAIN,SQL 起飛的基石


SUBQUERY:子查詢中的第一個 SELECT 查詢,不依賴於外部查詢的結果集

神奇的 SQL 之 MySQL 性能分析神器 →EXPLAIN,SQL 起飛的基石


DEPENDENT SUBQUERY:子查詢中的第一個select查詢,依賴於外部查詢的結果集

神奇的 SQL 之 MySQL 性能分析神器 →EXPLAIN,SQL 起飛的基石


DERIVED:派生表(臨時表),常見於 FROM 子句中有子查詢的狀況

神奇的 SQL 之 MySQL 性能分析神器 →EXPLAIN,SQL 起飛的基石


注意:MySQL5.7 中對 Derived table 作了一個新特性,該特性容許將符合條件的 Derived table 中的子表與父查詢的表合併進行直接JOIN,從而簡化簡化了執行計劃,同時也提升了執行效率;默認狀況下,MySQL5.7 中這個特性是開啓的,因此默認狀況下,上面的 SQL 的執行計劃應該是這樣的

神奇的 SQL 之 MySQL 性能分析神器 →EXPLAIN,SQL 起飛的基石


可經過 SET SESSION optimizer_switch='derived_merge=on|off' 來開啓或關閉當前 SESSION 的該特性。貌似扯的有點遠了(樓主你是否是在隨性發揮?),更多詳情能夠去查閱官網

MATERIALIZED:被物化的子查詢,MySQL5.6 引入的一種新的 select_type,主要是優化 FROM 或 IN 子句中的子查詢,更多詳情請查看:Optimizing Subqueries with Materialization

神奇的 SQL 之 MySQL 性能分析神器 →EXPLAIN,SQL 起飛的基石


UNCACHEABLE SUBQUERY:對於外層的主表,子查詢不可被緩存,每次都須要計算

神奇的 SQL 之 MySQL 性能分析神器 →EXPLAIN,SQL 起飛的基石


UNCACHEABLE UNION:相似於 UNCACHEABLE SUBQUERY,只是出如今 UNION 操做中

神奇的 SQL 之 MySQL 性能分析神器 →EXPLAIN,SQL 起飛的基石


SIMPLLE、PRIMARY、SUBQUERY、DERIVED 這 4 個在實際工做中碰到的會比較多,看得懂這 4 個就好了,至於其餘的,碰到了再去查資料就行了(我也想所有記住,但用的少,太容易忘記了,我也很無賴呀)

table

顯示了對應行正在訪問哪一個表(有別名就顯示別名),還會有 <union2,3> 、 <subquery2> 、 <derived2> (這裏的 2,三、二、2 指的是 id 列的值)相似的值,具體能夠往上看,這裏就不演示了(再演示就太長了,大家都看不下去了,那我不是白忙乎了 ?)

partitions

查詢進行匹配的分區,對於非分區表,該值爲NULL。大多數狀況下用不到分區,因此這一列咱們無需關注

type

關聯類型或者訪問類型,它指明瞭 MySQL 決定如何查找表中符合條件的行,這是咱們判斷查詢是否高效的重要依據(type 之於 EXPLAIN,就比如三圍之於女人!),完整介紹請看:explain-join-types

其值有多種,咱們以性能好到性能差的順序一個一個來看     

system

該表只有一行(=系統表),是 const 類型的特例
const

肯定只有一行匹配的時候,mysql 優化器會在查詢前讀取它而且只讀取一次,速度很是快。用於 primary key 或 unique 索引中有常亮值比較的情形

神奇的 SQL 之 MySQL 性能分析神器 →EXPLAIN,SQL 起飛的基石


eq_ref

對於每一個來自於前面的表的行,從該表最多隻返回一條符合條件的記錄。當鏈接使用的索引是 PRIMARY KEY 或 UNIQUE NOT NULL 索引時使用,很是高效

神奇的 SQL 之 MySQL 性能分析神器 →EXPLAIN,SQL 起飛的基石


ref

索引訪問,也稱索引查找,它返回全部匹配某個單個值的行。此類型一般出如今多表的 JOIN 查詢, 針對於非 UNIQUE 或非 PRIMARY KEY, 或者是使用了最左前綴規則索引的查詢,換句話說,若是 JOIN 不能基於關鍵字選擇單個行的話,則使用ref

神奇的 SQL 之 MySQL 性能分析神器 →EXPLAIN,SQL 起飛的基石


fulltext

當使用全文索引時會用到,這種索引通常用不到,會用專門的搜索服務(solr、elasticsearch等)來替代
ref_or_null

相似ref,可是添加了能夠專門搜索 NULL 的行

神奇的 SQL 之 MySQL 性能分析神器 →EXPLAIN,SQL 起飛的基石


這個是有前提條件的,前提爲 weapon 列有索引,且 weapon 列存在 NULL

index_merge

該訪問類型使用了索引合併優化方法

神奇的 SQL 之 MySQL 性能分析神器 →EXPLAIN,SQL 起飛的基石


這個一樣也是有條件的, id 列和 weapon 列都有單列索引。若是出現 index_merge,而且這類 SQL 後期使用較頻繁,能夠考慮把單列索引換爲組合索引,這樣效率更高

unique_subquery

相似於兩錶鏈接中被驅動表的 eq_ref 訪問方式,unique_subquery 是針對在一些包含 IN 子查詢的查詢語句中,若是查詢優化器決定將 IN 子查詢轉換爲 EXISTS 子查詢,並且子查詢可使用到主鍵或者惟一索引進行等值匹配時,則會使用 unique_subquery

神奇的 SQL 之 MySQL 性能分析神器 →EXPLAIN,SQL 起飛的基石


index_subquery

index_subquery 與 unique_subquery相似,只不過訪問子查詢中的表時使用的是普通的索引

神奇的 SQL 之 MySQL 性能分析神器 →EXPLAIN,SQL 起飛的基石


range

使用索引來檢索給定範圍的行,當使用 =、<>、>、>=、<、<=、IS NULL、<=>、BETWEEN 或者 IN 操做符,用常量比較關鍵字列時,則會使用 rang

神奇的 SQL 之 MySQL 性能分析神器 →EXPLAIN,SQL 起飛的基石


前提是必須基於索引,也就是 id 上必須有索引

index

當咱們可使用索引覆蓋,但須要掃描所有的索引記錄時,則會使用 index;進行統計時很是常見

神奇的 SQL 之 MySQL 性能分析神器 →EXPLAIN,SQL 起飛的基石


ALL

咱們熟悉的全表掃描

神奇的 SQL 之 MySQL 性能分析神器 →EXPLAIN,SQL 起飛的基石


possible_keys

展現在這個 SQL 中,可能用到的索引有哪些,但不必定在查詢時使用。若爲空則表示沒有可使用的索引,此時能夠經過檢查 WHERE 語句看是否能夠引用某些列或者新建索引來提升性能

key

展現這個 SQL 實際使用的索引,若是沒有選擇索引,則此列爲null,要想強制 MySQL 使用或忽視 possible_keys 列中的索引,在查詢中使用 FORCE INDEX、USE INDEX 或者I GNORE INDEX

key_len

展現 MySQL 決定使用的鍵長度(字節數)。若是 key 是 NULL,則長度爲 NULL。在不損失精確性的狀況下,長度越短越好

ref

展現的是與索引列做等值匹配的東東是個啥,好比只是一個常數或者是某個列。它顯示的列的名字(或const),此列多數時候爲 Null

rows

展現的是 mysql 解析器認爲執行此 SQL 時預計須要掃描的行數。此數值爲一個預估值,不是具體值,一般比實際值小

filtered

展現的是返回結果的行數所佔須要讀到的行(rows 的值)的比例,固然是越小越好啦

extra

表示不在其餘列但也很重要的額外信息。取值有不少,咱們挑一些比較常見的過一下

using index

表示 SQL 使用了使用覆蓋索引,而不用回表去查詢數據,性能很是不錯

神奇的 SQL 之 MySQL 性能分析神器 →EXPLAIN,SQL 起飛的基石


using where

表示存儲引擎搜到記錄後進行了後過濾(POST-FILTER),若是查詢未能使用索引,using where 的做用只是提醒咱們 mysql 要用 where 條件過濾結果集

神奇的 SQL 之 MySQL 性能分析神器 →EXPLAIN,SQL 起飛的基石


using temporary

表示 mysql 須要使用臨時表來存儲結果集,常見於排序和分組查詢

神奇的 SQL 之 MySQL 性能分析神器 →EXPLAIN,SQL 起飛的基石


表示 mysql 沒法利用索引直接完成排序(排序的字段不是索引字段),此時會用到緩衝空間(內存或者磁盤)來進行排序;通常出現該值,則表示 SQL 要進行優化了,它對 CPU 的消耗是比較大的

神奇的 SQL 之 MySQL 性能分析神器 →EXPLAIN,SQL 起飛的基石


查詢語句的WHERE子句永遠爲 FALSE 時將會提示該額外信息

神奇的 SQL 之 MySQL 性能分析神器 →EXPLAIN,SQL 起飛的基石


固然還有其餘的,不常見,等碰到了你們再去查吧(如今凌晨 1 點,我實在是太困了!)

總結

一、背景疑問

還記得客服小姐姐的問題嗎,她嫌咱們太慢,具體緣由下篇再詳細介紹,這裏就提一下:連表查詢的 鏈接鍵 類型不一致,一個 INT 類型,一個 VARCHAR 類型,致使 type 是 ALL(這誰設計的呀,坑死人呀! 難道是我 ?)

相關文章
相關標籤/搜索