mysql——查詢優化

一、Explain關鍵字查詢結果列的信息

二、Explain 關鍵字主要是描述可能用到的索引列是什麼;解析查詢優化器選擇的索引

   ① mysql是如何選擇索引的?

       1>首先會sql解析,優化join查詢,

        2>找到所有可能用到的索引列,

        3>分別計算全表掃描,可能用到索引列的成本

        4>選擇成本最低的進行查詢

    ② mysql查詢成本公式:成本 =  加載的頁數量*1.0(IO成本) + 查詢的數據量*0.2(CPU成本);

        IO成本:從磁盤加載到內存需要消耗的成本;

        CPU成本: 內存中根據條件過濾數據的成本;

        select * from user where id > '10101' and id < '20000' and to_date = '1991-10-10';(to_date列有二級索引);

        1> 計算全表掃描的成本: 執行sql SHOW TABLE STATUS LIKE  table_name;

          rows: 展示的是: 數據的行數,對於innoDB來說該值並不準確是一個估算值,對於MyISAM是準確的。

         Data_length:佔用的存儲空間字節數,使用MyISAM存儲引擎的表來說,該值就是數據文件的大小,對於使用InnoDB存 儲引擎的表來說,該值就相當於聚簇索引佔用的存儲空間大小,也就是說可以這樣計算該值的大小:

         Data_length = 聚簇索引頁面數*每個頁面的大小;所以頁面大小 = Data_length /16k/1024;

        所以全表掃描成本如下:

         I/O成本:1252*1 = 1252。1252指的是聚簇索引佔用的頁面數,1.0指的是加載一個頁面的成本常數。

         CPU成本:442070*0.2 = 88414。442070指的是統計數據中表的記錄數rows,對於InnoDB存儲引擎來說是一個估計值,0.2指的是訪問一條記錄所需的成本常數 總成本:1252+88414 = 89666。

      2> 計算主鍵索引的掃描成本: 執行sql SHOW TABLE STATUS LIKE  table_name;

      1、 計算IO成本:使用索引查詢時,默認是1(不管是=、in、>、<這些操作都需要從索引中確定一個範圍,不論這個範圍區間的 索引到底佔用了多少頁面,查詢優化器粗暴的認爲讀取索引的一個範圍區間的I/O成本和讀取一個頁面是相同的)。

     2、計算CPU成本:分別算出區間 的最左表一條記錄(最左區間),最右邊一條記錄(最右區間),從最左區間向後查找,如果到最右區間的距離小於10頁,則精確計算出記錄數,如果大於十頁,則估算每頁的記錄數,然後乘以頁數(根據父也的頁號計算),估算出總的記錄數。

     3> 計算二級索引的掃描成本: 執行sql SHOW TABLE STATUS LIKE  table_name;

       1、因爲通過二級索引查詢需要回表,所以在計算二級索引需要成本時還要加上回表的成本。回表的成本就相當於 下面這個SQL執行: select * from user where id in (主鍵值1,主鍵值2 .........);

        2、二級索引成本 = 二級索引成本 + 回表成本。

根據以上獲取到的成本選取最小的值作爲掃描條件,使用索引查詢數據。

 ③ 關於null值得處理,null在數據庫中有三種含義:

      1、nulls_equal 代表值相同。innoDB默認使用該含義。

       2、nulls_unequal 代表特殊值,所有的null值都是不同的值。

       3、nulls_ignored 不具有含義,統計數據時被忽略。

爲什麼說索引列中不要添加null值,在情況1中,null值過多,造成mysql 統計數據時認爲該列重複數據過多,在進行索引統計時,

重複數據過多,導致統計時 CPU成本高,(因爲估算的時候會估算每個值大概平均是多少),優化器認爲某個列中平均一個值重複次數特別多,所以傾向不使用該列做索引。

 ④ 關於 in查詢的處理。select * from user where name in(1,2,3,4,5,6,7,........);
   1、對於in查詢如果in查詢內的參數個數 <= 200(默認值是200,系統變量 eq_range_index_dive_limit決定,show variables like '%dive%'查看)時,mysql會逐一計算索引字段在表中的數據。

    2 、如果in查詢內的參數個數 > 200,比如1w,那麼mysql統計數據的成本就過高了,所以他會進行估算。

          首先 show index from user(表名) 的cardinality列能夠顯示出,對應的索引列中大概不重複的元素的個數(就是有多少種數據), 使用 SHOW TABLE STATUS LIKE 'user';(表名)  中的rows能夠展示出數據表中大概有多少列(這兩個統計對於innodb引擎來說都是不準的)。這樣 rows /  cardinality ,大概就可以計算出每索引記錄對應的行數,再 乘以 in中的參數個數來統計回表的成本。in參數個數 * rows /  cardinality。

 

三、join查詢1、基於塊的嵌套循環鏈接(驅動表數據加載到 join buffer 匹配被驅動表數據,減少IO成本)

     掃描表的過程其實是先把表的數據從磁盤加載到內存,在內存中做匹配,對於join查詢,比如一下sql:

   select * from t1 join t2 on t1.a=t2.a where t1.b in(1,2);查詢過程是先跟據where條件查找出t1表裏的數據,然後再把被驅動表的所有數據與驅動表的數據一一匹配,每匹配驅動表的一條數據就需要加載查詢所有的被驅動表,這樣IO成本就比較高,所以mysql有一個join buffer的概念,join buffer 就是進行連接查詢時的一塊空間,他會緩存驅動表的 數據查詢列 和 查詢條件列,加載的被驅動表數據進行多條件匹配,增快匹配速度。這就是爲什麼查詢時儘量不要使用 *的原因。這個join buffffer的大小是可以通過啓動參數或者系統變量join_buffffer_size進行配置,默認大小爲262144字節(也 就是256KB),最小可以設置爲128字節。

2、外連接消除

    對於內連接mysql優化器會優化 它的執行順序,但是left join和right join因爲順序固定則不會優化。

         內外連接區別:如果被驅動表中沒有 on條件的數據,內連接會捨棄該數據,而外連接則會用null補充值。

3、派生表 select * from (select a,b from t1) as t;(我理解他就是臨時表不知道對不對)

   ① 上面這種查詢就會生成派生表 t ,有a,b兩個字段。

   ②  我們可以將派生表的結果集寫到一個內部的臨時表中,然後就把這個物化表當作普通表一樣參與查詢。當然,在 對派生表進行物化時,使用了一種稱爲延遲物化的策略,也就是在查詢中真正使用到派生表時纔回去嘗試物化派 生表,而不是還沒開始執行查詢就把派生表物化掉。比如:如果派生表查出來的數據爲空則不需要物化。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章