天天看點

Mysql性能優化之Explain關鍵字

一、何謂索引?

​ 索引,簡單說,就是排好序,查找的快的一種資料結構;

二、sql為何慢?

​ 1、查詢語句寫的爛;

​ 2、索引失效:(單值、複合)

​ 3、關聯查詢太多

​ 4、伺服器調優(緩沖、線程數)

三、索引優勢與劣勢;

​ 優勢:提高檢索效率,降低IO成本;降低資料的排序成本,降低CPU的消耗;

​ 劣勢:索引也是一張表,該表儲存了主鍵與索引字段,并指向實體表的記錄,是以索引也需要占用空間,其次,在更新表時,不僅要更新資料,還要更新索引;

四、索引分類?

​ 單值索引:隻包含一個列;

​ 唯一索引:索引列的值必須時唯一的,不過可以有空值;

​ 複合索引:一個索引包含多個列;

五、主角——explain

​ 1、是什麼?

​ 使用explain可以模拟優化器執行sql語句,進而知道mysql是如何執行sql語句的

​ 2、能幹嘛?

​ 表的讀取順序、資料讀取操作的操作類型、那些索引可以使用、那些索引被實際使用、表之間的引用、每張表有多少行被優化器查詢

​ 3、怎麼用?

​ explain + sql

​ 4、長什麼樣?

​ [外鍊圖檔轉存失敗,源站可能有防盜鍊機制,建議将圖檔儲存下來直接上傳(img-UCwcFue8-1595842967023)(/Applications/Typora.app/Contents/Resources/TypeMark/Docs/img/WechatIMG4.jpeg)]

​ id: select 查詢的編号,表示查詢順序:

​ id相同,順序執行,由上到下;

​ id不同,如果是自查詢,ID的序号會增加,ID值越大,優先級越高,越先被執行

​ select_type:有哪些?

​ simple、primary、subquery、derived、union、union result;

​ 作用?

​ simple:簡單查詢,不包含任何自查詢和關聯查詢;

​ EXPLAIN SELECT * from PROJECT where id = 1

​ primary:查詢中包含任何複雜的子部分,最外層查詢則被标記為primary

​ EXPLAIN SELECT * from BUILDING b LEFT join (select building_id,sum(UNOCCUPIED_AREA) as area from RENTAL_UNIT ru group by building_id having area > 40000 ) r on r.building_id = b.ID where b.ID > 13231

​ subquery:在select或者where字句中包含的複雜子查詢

​ EXPLAIN SELECT (select Id from PROJECT ) from DUAL

​ EXPLAIN select * from DEMAND_SITE_VISIT where CREATE_TIME > (SELECT MAX(CREATE_TIME) from DEMAND_FOLLOW_LOG )

​ derived:在from中包含子查詢被标記為derived,mysql會遞歸執行這些子查詢,把結果放在臨時表裡面;

​ EXPLAIN SELECT * from BUILDING b LEFT join (select building_id,sum(UNOCCUPIED_AREA) as area from RENTAL_UNIT ru group by building_id having area > 40000 )r on r.building_id = b.ID

​ where b.ID > 13231

​ union:如果第二個select出現在union後,則會被标記為union,如果union包含在from子句的子查詢中,則外層的select被标記為derived;

​ explain SELECT ID FROM DEMAND UNION select REMARK from DEMAND_FOLLOW_LOG

​ union result:從union結果中擷取的select

​ explain SELECT ID FROM DEMAND UNION select REMARK from DEMAND_FOLLOW_LOG

​ table:顯示操作的是哪一張表;

​ type:system>const>eq_ref>ref>ref_or_null>index_merge>unique_subquery>index_subquery>range>index>all

​ System:表中隻有一條記錄,這是const的執行個體,可以忽略

​ explain select * from (select id from PROJECT where id = 15) a

​ const:通過索引一次就可以找到了,一般出現在primary key或者唯一索引的地方,是以快得很;

​ explain select * from PROJECT where id = 15

​ eq_ref:簡單說,用于連結清單查詢,按照聯表的主鍵或者唯一建删除,讀取本表和關聯聯表中的每行組合成的一行,eq_ref可以使用=比對索引的列,比較值可以是常量或者使用此表之前讀取的表中的列的表達式,當連接配接使用索 引的所有部分時,索引是主鍵或唯一非null索引時,将使用該值;

​ explain SELECT * FROM PROJECT p,BUILDING b where p.ID = b.PROJECT_ID and p.CITY_CODE = '310100’

​ ref:非唯一性索引掃描,傳回某個比對單獨值的所有行,本質上也是一種索引通路,它傳回所有比對某個單獨值的行,然而,它可能會找到多個符合條件的行,是以他應該屬于查找和掃描的混合體;

​ EXPLAIN SELECT * from FUNNEL_REPORT where UTM_SOURCE = 'baidu’;

​ explain SELECT * from PROJECT p LEFT join BUILDING b on p.ID = b.PROJECT_ID

​ range:隻檢索給定範圍的行,使用一個索引來選擇行,key列顯示使用了哪個索引,一般就是在where語句中出現了between、<、>、in等查詢語句,這種範圍掃描 索引比全表掃描要好,因為它隻需要開始于索引的某一點,而結束于另一點;

​ explain SELECT * from PROJECT where id > 1890

​ Index merge:where子句包含多個條件(and 或者or連接配接),mysql在5.1開始,開始了index merge,其實就是對多個索引列進行條件掃描,然後将各個結果進行合并;

​ index:full index scan,index與ALL差別為index類型隻周遊索引樹,這通常比ALL快,因為索引檔案通常比資料檔案小;

​ all:full table scan,将便利全表以找到比對的行

​ 一般來說,要保證查詢至少達到range級别,最好能達到ref;

​ possible_keys:顯示可能應用在這張表中的索引,一個或多個,查詢涉及到字段上若存在的索引,則該索引将被列出,但不一定被查詢實際用到;

​ key:實際使用的索引,如果為null,則沒有使用索引,查詢中若使用了覆寫索引,則該索引和查詢的select字段重疊;

​ explain SELECT MOBILE_NO,STATUS from CONTACT where NAME = ‘f123dfff’ and

TYPE

=4

​ key_len:表示索引中使用的位元組數,可通過該列計算查詢中使用的索引長度,在不損失精确性的情況下,長度越短越好,顯示的值為索引字段的最大可能長度,是一個常數,并非實際使用長度,即key_len是根據表定義計算而得,不是通過表内檢索出的;(可計算 default null:1位元組、varchar:2位元組、tinyint:1位元組)

​ ref:顯示索引的哪一列被使用了,如果可能的話,是一個常數,那些列或常量被用于查找索引列上的值;

​ rows:根據表統計資訊及索引選用的情況,大緻估算出找到所需的記錄需要讀取的行數;

​ extra:

​ using filesort:說明mysql會對資料使用一個外部的索引排序,而不是按照表内的索引順序進行讀取,mysql中無法利用索引完成的排序操作稱為檔案排序;

​ explain SELECT MOBILE_NO,STATUS from CONTACT where NAME = ‘f123dfff’ and type = 4 order by MOBILE_NO

​ using temporary:使用了臨時表儲存中間結果,mysql在對查詢結果排序時使用臨時表,常見于排序orderby和分組group by;

​ explain SELECT * from CONTACT group by

TYPE

​ using index:表示相應的select操作中使用了覆寫索引,避免通路了表的資料行,效率不錯,如果同時出現using where,表明索引被用來執行索引鍵值的查找,如果沒有同時出現using where,表明索引用來讀取資料而非執行查找動作;

​ explain SELECT MOBILE_NO,STATUS from CONTACT where NAME = ‘f123dfff’ and type = 4 order by MOBILE_NO DESC

​ using where:表明使用了where過濾;

​ explain SELECT MOBILE_NO,STATUS from CONTACT where NAME = ‘f123dfff’ and type = 4 order by MOBILE_NO DESC

​ using join buffer:使用了連接配接緩存;隻有當join類型為all,index,range後者是index_merge的時候才會使用join buffer;

​ impossible where :where子句的值總是false,不能用來擷取仍和元組;

​ explain SELECT MOBILE_NO,STATUS from CONTACT where 1=2

​ select table optimized away:在沒有groupby子句的情況下,基于索引優化min/max操作或者對于myisam存儲引擎優化count(*)操作,不必等到執行階段再進行計算,查詢計劃生成的階段即完成優化;

​ distinct:優化distinct操作,在找到第一比對的元組後即停止找同樣值的動作;

索引失效與優化:

​ 1、全部比對最佳

​ 2、最佳最字首法則(如果索引了多列,要遵守最左字首法則,指的是查詢從索引的最左前列開始并且不跳過索引中的列)

​ 3、不在索引列上做任何操作(計算、函數、類型轉換)

​ 4、(避免select *)盡量使用覆寫索引

​ 5、存儲引擎不能使用索引中範圍條件右邊的列

​ 6、mysql在使用不等于的時候無法使用索引會導緻全表掃描

​ 7、is null,is not null也無法使用索引

​ 8、like以通配符開頭(“%abc”)mysql索引失效會變成全表掃描的操作

​ 9、字元串不加單引号索引會失效

​ 10、少用or,用它來連接配接時會索引失效

一般性的建議:

​ 對于單值索引,盡量選擇針對目前的query過濾性更好的索引;

​ 在選擇組合索引的時候,目前query中過濾性最好的字段在索引字段順序中,位置越靠前越好;

​ 在選擇組合索引的時候,盡量選擇可以能夠包含目前query中的where字句中更多字段的索引;

​ 盡可能通過分析統計資訊和調整query的寫法來達到選擇合适索引的目的;

查詢截取分析:

查詢優化:

​ 永遠小表驅動大表

​ order by關鍵字優化:盡量使用index方式排序,避免使用filesort方式排序;

​ 盡可能在索引列上完成排序操作,遵照索引建立最佳左字首;

​ 如果不在索引列上,filesort有兩種算法,mysql就要啟動雙路排序和單路排序;

​ 雙路排序:mysql4.1之前使用雙路排序,字面意思是兩次掃描磁盤,最終得到資料,讀取行指針和orderby列,對他們進行排序,然後掃描已經排序好的清單,按照清單中的值重新從清單中讀取對應的資料輸出,從磁盤取排序字段,在buffer進行排序,再從磁盤取其他字段;

​ 單路排序:從磁盤讀取查詢需要的所有列,按照orderby列在buffer對他們進行排序,然後掃描排序後的清單進行輸出;它的效率更快一些,避免了第二次讀取資料,并且把随機IO變成了順序IO,但是它會使用更多的空間;

優化政策:

​ 增大sort_buffer_size參數的設定;

​ 增大max_length_for_sort_data參數的設定;

groupby關鍵字的優化:

​ groupby實質是先排序後進行分組,遵照索引建立的最佳左字首;

​ 當無法使用索引列,增大max_length_for_sort_data和sort_buffer_size參數的設定;

​ where高于having,能寫where限定的條件就不要去having限定了;

執行個體:

​ 1、SELECT COUNT(DISTINCT a.ID) AS COUNT

​ FROM PROJECT a

​ LEFT JOIN LEADS d ON d.PROJECT_ID = a.ID

​ LEFT JOIN PROJECT_COMPANY_ASSN pc ON pc.PROJECT_ID = a.ID

​ LEFT JOIN OWNER c ON c.COMPANY_ID = pc.COMPANY_ID AND pc.STATUS = 1 AND pc.TYPE = 3

​ WHERE d.STATUS != 12 AND a.STATUS in ( 0 ) AND d.STATUS = 7

​ 2、SELECT

​ COUNT(1) AS VIEW_COUNT,

​ pd.PROJECT_ID

​ FROM PROJECT_DETAIL_VIEW_LOG pd

​ INNER JOIN PROJECT p

​ ON p.ID = pd.PROJECT_ID

​ WHERE p.PROVINCE_CODE = ‘320281’

​ OR p.CITY_CODE=‘320281’

​ OR p.DISTRICT_CODE=‘320281’

​ GROUP BY pd.PROJECT_ID

​ ORDER BY VIEW_COUNT DESC

​ LIMIT 0,10