Mysql如何分析慢查詢及優化(一)--- EXPLAIN詳解

慢查詢優化是勢在必行的,可是要對mysql慢查詢進行優化,首先要知道慢查詢的語句和mysql數據庫的運行狀態。mysql

對數據庫慢查詢進行排查以前先了解一下mysql的幾個命令,有助於幫助咱們定位慢查詢語句:sql

show status;  // 查詢mysql數據庫的一些運行狀態
show status like 'uptime'; // 查看mysql數據庫啓動多長時間,myisam存儲引擎長時間啓動須要進行碎片整理
show status like 'slow_queries'; //查看慢查詢的數量
show variables like 'long_query_time';//查看慢查詢的時間
set long_query_time = 0.5;//設置慢查詢的時間,單位秒(s)
 
通常咱們對慢查詢語句進行優化,須要分析定位語句的執行狀況,mysql的Explain命令能夠很好的幫助咱們,下邊咱們對Explain進行詳解:
 
1.用法:
 
Explain select * from notes where id = 137 ;//將Explain命令放到查詢命令前便可

  返回的結果以下:數據庫

 

2.返回結果字段說明:緩存

id:SELECT的查詢序列號。
select_type:表示SELECT語句的類型。有如下幾種取值: SIMPLE:表示簡單杳詢,其中不包括鏈接查詢和子查詢; PRIMARY:表示主查詢,或者最外層的查詢語句; UNION:表示鏈接查詢的第2個或後面的查詢語句; DEPENDENT UNION:鏈接查詢中的第2個或後面的SELECT語句,取決於外面的查詢; UNION RESULT:鏈接查詢的結果; SUBQUERY:子查詢中的第一個SELECT語句; DEPENDENT SUBQUERY:子查詢中的第一個SELECT,取決於外面的查詢; DERIVED:導出表的SELECT (FROM語句的子查詢)。
table:表示查詢的表。
type:表示表的鏈接類型。下面按照從最優類型到最差類型的順序給出各類鏈接類型: (1) system 該表僅有一行的系統表。這是const鏈接類型的一個特例。 (2) const 數據表最多隻有一個匹配行,它將在查詢開始時被讀取,並在餘下的査詢優化中做爲常量對待。
     const表查詢速度很快,由於它們只讀取一次。const用於使用常數值比較PRIMARY KEY或UNIQUE索引的全部部分的場合。 好比: SELECT
* from tb1_name WHERE primary_key=1; SELECT * from tb1_name WHERE primary_key_part1=1 AND primary_key_part2=2 (3) eq_ref 對於每一個來自前面的表的行組合,從該表中讀取一行。當一個索引的全部部分都在查詢中使用,而且索引是UNIQUE或者PRIMARY KEY時,便可使用這種類型。 eq_ref能夠用於使用「=」操做符比較帶索引的列。比較值能夠爲常量或者一個在該表前面所讀取的表的列的表達式。 好比: SELECT * FROM ref_table,other_table WHERE ref_table.key_cloumn = other_table.cloumn; SELECT * FROM ref_table,other_tbale WHERE ref_table.key_cloumn_part1 = other_table.cloumn AND ref_table.key_cloumn_part2 = 1; (4)ref 對於來自前面的表的任意組合,將從該表中讀取全部匹配的行。
    這種類型用於索引既不是UNIQUE也不是PRIMARY KEY的狀況,或者查詢中使用了索引列在左子集,既索引中左邊的部分列組合。ref能夠用於使用
=或者<=>操做符的帶索引的列。 好比: select * from ref_table where key_column=expr; select * from ref_table,other_table where ref_table.key_column=other_table.column; select * from ref_table,other_table where ref_table.key_column_part1=other_table.column and ref_table.key_column_part2=1; (5)ref_or_null 這種鏈接類型相似ref,不一樣的是mysql會在檢索的時候額外的搜索包含null值的記錄。在解決子查詢中常用該連接類型的優化。 好比: select * from ref_table where key_column=expr or key_column is null; (6)index_merge 該連接類型表示使用了索引合併優化方法。在這種狀況下,key列包含了使用的索引的清單,key_len包含了使用的索引的最長的關鍵元素。 (7)unique_subquery 該類型替換了下面形式的IN子查詢的ref: value in (select primary_key from single_table where some_expr) (8)index_subquery 這種鏈接類型相似 unique_subquery。能夠替換IN子查詢,不過它用於在子查詢中沒有惟一索引的狀況下, 例如如下形式: value in (select key_column from single_table where some_expr) (9)range 只檢索給定範圍的行,使用一個索引來選擇行。key列顯示使用了哪一個索引。ken_len包含所使用索引的最長關鍵元素。
    當使用
=, <>, >,>=, <, <=, is null, <=>, between, 或 in操做符,用常量比較關鍵字列時,類型爲range。 下面介紹幾種檢索制定行的狀況: select * from tbl_name where key_column = 10; select * from tbl_name where key_column between 10 and 20; select * from tbl_name where key_column in (10,20,30); select * from tbl_name where key_part1= 10 and key_part2 in (10,20,30); (10)index 鏈接類型跟ALL同樣,不一樣的是它只掃描索引樹。它一般會比ALL快點,由於索引文件一般比數據文件小。 (11)ALL 對於前面的表的任意行組合,進行完整的表掃描。
     若是第一個表沒有被標識爲const的話就不大好了,在其餘狀況下一般是很是糟糕的。正常地,能夠經過增長索引使得能從表中更快的取得記錄以免ALL。
possible_keys:是指MySQL在搜索表記錄時可能使用哪一個索引。
  若是這個字段的值是NULL,就表示沒有索引被用到。
  這種狀況下,就能夠檢查WHERE子句中哪些字段哪些字段適合增長索引以提升查詢的性能。
  建立一下索引,而後再用explain 檢查一下。
 key:顯示MySQL實際上要用的索引。
  當沒有任何索引被用到的時候,這個字段的值就是NULL。
  想要讓MySQL強行使用或者忽略在 possible_keys字段中的索引列表,能夠在查詢語句中使用關鍵字force index
, use index或 ignore index。參考SELECT語法。
key_len:顯示mysql使用索引的長度。當key 字段的值爲NULL時,索引的長度就是NULL。注意,key_len的值能夠告訴你在聯合索引中MySQL會真正使用了哪些索引。
ref:表示使用哪一個列或常數與索引一塊兒來查詢記錄。
rows:顯示MySQL在表中進行查詢時必須檢查的行數。
Extra:顯示查詢中mysql的附加信息。如下是這個字段的幾個不一樣值的解釋:
    (1) distinct MySQL當找到當前記錄的匹配聯合結果的第一條記錄以後,就再也不搜索其餘記錄了。
(2) not exists MySQL在查詢時作一個LEFT JOIN優化時,當它在當前表中找到了和前一條記錄符合LEFT JOIN條件後,就再也不搜索更多的記錄了。
     好比:select
* from t1 left join t2 on t1.id=t2.id where t2.id is null; 假使 t2.id 定義爲 not null
     這種狀況下,MySQL將會掃描表 t1而且用 t1.id 的值在 t2 中查找記錄。
     當在 t2中找到一條匹配的記錄時,這就意味着 t2.id 確定不會都是null,就不會再在 t2 中查找相同id值的其餘記錄了。
     也能夠這麼說,對於 t1 中的每一個記錄,mysql只須要在t2 中作一次查找,而無論在 t2 中實際有多少匹配的記錄。 (3)
range checked for each record (index map: #) mysql沒找到合適的可用的索引。
     取代的辦法是,對於前一個表的每個行鏈接,它會作一個檢驗以決定該使用哪一個索引(若是有的話),而且使用這個索引來從表裏取得記錄。
     這個過程不會很快,但總比沒有任何索引時作錶鏈接來得快。 (4)using filesort MySQL須要額外的作一遍從已排好的順序取得記錄。
     排序程序根據鏈接的類型遍歷全部的記錄,而且將全部符合where條件的記錄的要排序的鍵和指向記錄的指針存儲起來。
     這些鍵已經排完序了,對應的記錄也會按照排好的順序取出來。

(5)using index 字段的信息直接從索引樹中的信息取得,而再也不去掃描實際的記錄。這種策略用於查詢時的字段是一個獨立索引的一部分。 (6)using temporary mysql須要建立臨時表存儲結果以完成查詢。這種狀況一般發生在查詢時包含了group by和order by子句,它以不一樣的方式列出了各個字段。 (7)using where where子句將用來限制哪些記錄匹配了下一個表或者發送給客戶端。
     除非你特別地想要取得或者檢查表種的全部記錄,不然的話當查詢的extra字段值不是using where而且錶鏈接類型是all或index時可能表示有問題。 若是你想要讓查詢儘量的快,那麼就應該注意extra字段的值爲using filesort和using temporary的狀況。

 

 3.索引沒起做用的狀況:
  (1) 使用LIKE關鍵字的查詢語句
        在使用LIKE關鍵字進行查詢的查詢語句中,若是匹配字符串的第一個字符爲「%」,索引不會起做用。只有「%」不在第一個位置索引纔會起做用。
   (2)使用多列索引的查詢語句
        MySQL能夠爲多個字段建立索引。一個索引最多能夠包括16個字段。對於多列索引,只有查詢條件使用了這些字段中的第一個字段時,索引纔會被使用。
   (3)使用OR關鍵字的查詢語句
        查詢語句的查詢條件中只有OR關鍵字,且OR先後的兩個條件中的列都是索引時,查詢中才會使用索引。不然,查詢將不使用索引。
 
4.優化數據庫結構:
        合理的數據庫結構不只可使數據庫佔用更小的磁盤空間,並且可以使查詢速度更快。數據庫結構的設計,須要考慮數據冗餘、查詢和更新的速度、字段的數據類型是否合理等多方面的內容。

  (1)  分解表服務器

  對於字段比較多的表,若是有些字段的使用頻率很低,能夠將這些字段分離出來造成新表。由於當一個表的數據量很大時,會因爲使用頻率低的字段的存在而變慢。數據庫設計

   (2) 增長中間表
        對於須要常常聯合查詢的表,能夠創建中間表以提升查詢效率。經過創建中間表,把須要常常聯合查詢的數據插入到中間表中,而後將原來的聯合查詢改成對中間表的查詢,以此來提升查詢效率。
 
   (3) 合理冗餘字段
        設計數據庫表時應儘可能遵循範式理論的規約,儘量減小冗餘字段,讓數據庫設計看起來精緻、優雅。可是合理加入冗餘字段能夠提升查詢速度。 
   
   (4) 切分查詢
        好比咱們要刪除舊的數據,可能須要一次性刪除不少數據會鎖住不少數據、佔滿整個事務日誌、耗盡系統資源、阻塞不少小的但重要的查詢。將一個大的DELETE語句切成多個較小的查詢能夠儘量小的影響MySQL性能,同時還能夠減小MySQL複製的延遲,以下:
        DELETE FROM table WHERE create_date < NOW(); 
        替換爲
        rows_affected = 0
        do { 
            rows_affected = do_query(
                "DELETE FROM table WHERE create_date < NOW() LIMIT 10000"    
            )
        } while rows_affected > 0
        一次刪除一萬行數據通常來講是一個比較高效並且對服務器影響也最小的作法,若是每次暫停一會會更好。
 
      (5) 分解關聯查詢
  將關聯查詢分解爲多個小查詢是頗有必要的。就是能夠對每個表進行一次單表查詢,而後將查詢結果在應用程序中進行關聯.例如:
        SELECT * FROM tag 
        JOIN tag_post ON tag_id = tag.id
        JOIN post ON tag_post.post_id = post.id
        WHERE tag.tag = 'mysql';
        分解爲:
        SELECT * FROM tag WHERE tag = 'mysql';
        SELECT * FROM tag_post WHERE tag_id = 1234;
        SELECT * FROM post WHERE post.id in (123,456,567);
        有不少場景均可以使用:好比當應用可以方便地緩存單個查詢的結果的時候、當能夠將數據分佈到不一樣MySQL服務器上的時候、當可以使用IN()的方式代替關聯查詢的時候、當查詢中使用同一個數據表的時候。
相關文章
相關標籤/搜索