1. 程式人生 > >【MySql】Sql優化(三)——效能優化

【MySql】Sql優化(三)——效能優化

一、前言

      當資料庫資料達到一定數量的時候,結合資料庫連線池Druid的視覺化監控介面,對系統中執行的sql語句進行檢測,對使用頻繁、執行時間長的sql語句進行優化。

二、優化方案原則

  • [原則一:選擇需要優化的SQL]

    1,選擇需要優化的SQL:不是所有的SQL都需要優化,在優化的過程中,首選更需要優化的SQL;
    2,SQL執行較慢有兩個影響原因,IO和CPU,明確性能瓶頸所在;
    3,明確優化目標;

  • [原則二:從Explain和Profile入手]

    1,任何SQL的優化,都從Explain語句開始;Explain語句能夠得到資料庫執行該SQL選擇的執行計劃;
    2,首先明確需要的執行計劃,再使用Explain檢查;
    3,使用profile明確SQL的問題和優化的結果;

  • [原則三:永遠用小結果集驅動大的結果集]

  • [原則四:在索引中完成排序]

  • [原則五:使用最小Columns]

    1,減少網路傳輸資料量;
    2,特別是需要使用column排序的時候.為什麼?MYSQL排序原理,是把所有的column資料全部取出,在排序快取區排序,再返回結果;如果column資料量大,排序區容量不夠的時候,就會使用先column排序,再取資料,再返回的多次請求方式;

  • [原則六:使用最有效的過濾條件]

    1,過多的WHERE條件不一定能夠提高訪問效能;
    2,一定要讓where條件使用自己預期的執行計劃;

  • [原則七:避免複雜的JOIN和子查詢]

    1,複雜的JOIN和子查詢,需要鎖定過多的資源,MYSQL在大量併發情況下處理鎖定效能下降較快;
    2, 不要過多依賴SQL的功能,把複雜的SQL拆分為簡單的SQL;
    3,MySQL子查詢效能較低,應儘量避免使用;

三、優化思路

3.1 各個系統的Druid監控介面

      舉例說明:
      以基礎為例,開啟測試網址

這裡寫圖片描述

      按照sql執行次數排序,選擇執行時間(單位是微妙,1秒(s)=1000毫秒(ms)),挑選執行時間過長的sql進行優化。
優化方向從兩個方向優化:1.SQL語句 2.索引優化

這裡寫圖片描述

3.2 SQL語句優化

3.2.1 Where子句優化

  • 刪除不必要的括號
((a AND b) AND c OR (((a AND b) AND (c AND d))))
-> (a AND b AND c) OR (a AND b AND c AND
d)
  • 持續摺疊:
(a<b AND b=c) AND a=5
-> b>5 AND b=c AND a=5
  • 恆定的條件去除(因為不斷的摺疊)
(B>=5 AND B=5) OR (B=6 AND 5=5) OR (B=7 AND 5=6)
-> B=5 OR B=6

3.2.2 Join優化

       A JOIN B:通過A表的結果集作為迴圈基礎,一條一條的通過結果集中的資料作為過濾條件到下一個表中查詢資料,然後合併結果;

      JOIN的優化原則:

1,儘可能減少Join 語句中的Nested Loop 的迴圈總次數,用小結果集驅動大結果集; 注意不是:小表連線大的快,而是結果集
2,優先優化Nested Loop 的內層迴圈; 做索引
3,保證Join 語句中被驅動表上Join 條件欄位已經被索引;
4,擴大join buffer的大小;

      舉例:

這裡寫圖片描述

3.3 explain檢視執行計劃

      Explain命令可以讓我們檢視MYSQL執行一條SQL所選擇的執行計劃;

3.3.1 使用方式

explain SQL;

      舉例:

這裡寫圖片描述

3.3.2 返回欄位說明

1.ID:執行查詢的序列號;

2.select_type:使用的查詢型別

1,DEPENDENT SUBQUERY:子查詢中內層的第一個SELECT,依賴於外部查詢的結果集;
2,DEPENDENT UNION:子查詢中的UNION,且為UNION 中從第二個SELECT 開始的後面所有SELECT,同樣依賴於外部查詢的結果集;
3,PRIMARY:子查詢中的最外層查詢,注意並不是主鍵查詢;
4,SIMPLE:除子查詢或者UNION 之外的其他查詢;
5,SUBQUERY:子查詢內層查詢的第一個SELECT,結果不依賴於外部查詢結果集;
6,UNCACHEABLE SUBQUERY:結果集無法快取的子查詢;
7,UNION:UNION 語句中第二個SELECT 開始的後面所有SELECT,第一個SELECT 為PRIMARY
8,UNION RESULT:UNION 中的合併結果;

3.table:這次查詢訪問的資料表;

4.type:對錶所使用的訪問方式:

1,all:全表掃描
2,const:讀常量,且最多隻會有一條記錄匹配,由於是常量,所以實際上只需要讀一次;
3,eq_ref:最多隻會有一條匹配結果,一般是通過主鍵或者唯一鍵索引來訪問;
4,fulltext:全文檢索,針對full text索引列;
5,index:全索引掃描;
6,index_merge:查詢中同時使用兩個(或更多)索引,然後對索引結果進行merge 之後再讀取表資料;
7,index_subquery:子查詢中的返回結果欄位組合是一個索引(或索引組合),但不是一個主鍵或者唯一索引;
8,rang:索引範圍掃描;
9,ref:Join 語句中被驅動表索引引用查詢;
10,ref_or_null:與ref 的唯一區別就是在使用索引引用查詢之外再增加一個空值的查詢;
11,system:系統表,表中只有一行資料;
12,unique_subquery:子查詢中的返回結果欄位組合是主鍵或者唯一約束;

5.possible_keys:可選的索引;如果沒有使用索引,為null;

6.key:最終選擇的索引;

7.key_len:被選擇的索引長度;

8.ref:過濾的方式,比如const(常量),column(join),func(某個函式);

9.rows:查詢優化器通過收集到的統計資訊估算出的查詢條數;

10.Extra:查詢中每一步實現的額外細節資訊

1,Distinct:查詢distinct 值,所以當mysql 找到了第一條匹配的結果後,將停止該值的查詢而轉為後面其他值的查詢;
2,Full scan on NULL key:子查詢中的一種優化方式,主要在遇到無法通過索引訪問null值的使用使用;
3,Impossible WHERE noticed after reading const tables:MySQL Query Optimizer 通過收集到的統計資訊判斷出不可能存在結果;
4,No tables:Query 語句中使用FROM DUAL 或者不包含任何FROM 子句;
5,Not exists:在某些左連線中MySQL Query Optimizer 所通過改變原有Query 的組成而使用的優化方法,可以部分減少資料訪問次數;
6,Select tables optimized away:當我們使用某些聚合函式來訪問存在索引的某個欄位的時候,MySQL Query Optimizer 會通過索引而直接一次定位到所需的資料行完成整個查詢。當然,前提是在Query 中不能有GROUP BY 操作。如使用MIN()或者MAX()的時候;
7,Using filesort:當我們的Query 中包含ORDER BY 操作,而且無法利用索引完成排序操作的時候,MySQL Query Optimizer 不得不選擇相應的排序演算法來實現。
8,Using index:所需要的資料只需要在Index 即可全部獲得而不需要再到表中取資料;
9,Using index for group-by:資料訪問和Using index 一樣,所需資料只需要讀取索引即可,而當Query 中使用了GROUP BY 或者DISTINCT 子句的時候,如果分組欄位也在索引中,Extra 中的資訊就會是Using index for group-by;
10,Using temporary:當MySQL 在某些操作中必須使用臨時表的時候,在Extra 資訊中就會出現Using temporary 。主要常見於GROUP BY 和ORDER BY 等操作中。
11,Using where:如果我們不是讀取表的所有資料,或者不是僅僅通過索引就可以獲取所有需要的資料,則會出現Using where 資訊;
12,Using where with pushed condition:這是一個僅僅在NDBCluster 儲存引擎中才會出現的資訊,而且還需要通過開啟Condition Pushdown 優化功能才可能會被使用。控制引數為engine_condition_pushdown 。

3.4 索引優化

      通過使用Explain SQL檢視sql的執行計劃,可以看出sql中是否是按照程式設計師的預想計劃執行的,如果不是,需要我們再次對sql進行優化,新增索引是最快,最有效果的優化。

      預設情況下,一旦建立了一個表,這個表設定了主鍵,那麼MYSQL會自動的為這個主鍵建立一個unique的索引。

      根據sql語句的執行計劃,具體分析新增單列索引還是符合索引。

3.4.1 舉例說明

      表結構如下:三列,不帶索引,儲存100w資料。

這裡寫圖片描述 這裡寫圖片描述

      執行sql查詢:SELECT * FROM t_school WHERE address =’大米時代9’
      用時0.864s

這裡寫圖片描述

      通過explain命令查詢執行計劃:
      分析結果:採用全表掃描,沒有使用索引,查詢總行數為997002行

這裡寫圖片描述

      對address新增索引後:

這裡寫圖片描述

      查詢:用時0.010s,速度提升了86倍。(有一定因素是,快取影響的)

這裡寫圖片描述

      使用explain分析執行計劃:使用了index_address索引,查詢了1999行,查詢行數明顯變少

這裡寫圖片描述

3.4.2 索引的使用限制

1.BLOB 和TEXT 型別的列只能建立字首索引

2.MySQL 目前不支援函式索引(在MYSQL中,索引只能是一個列的原始值,不能把列通過計算的值作為索引);

例項:請查詢1981年入職的員工:

 SELECT * FROM emp WHERE year(hire_date)='1981';

問題:查詢的列是在過濾之前經過了函式運算;所以,就算hire_date作為索引,year(hire_date)也不會使用索引;
解決方案:
1,SELECT * FROM emp WHERE hire_date BETWEEN ‘1981-01-01’ AND ‘1981-12-31’;
2,在建立一列,這列的值是year(hire_date),然後把這列的值作為索引;

3.使用不等於(!= 或者<>)的時候MySQL 無法使用索引

4.過濾欄位使用了函式運算後(如abs(column)),MySQL 無法使用索引

5.Join 語句中Join 條件欄位型別不一致的時候MySQL 無法使用索引

6.使用LIKE 操作的時候如果條件以萬用字元開始( ‘%abc…’)MySQL 無法使用索引

1,字串是可以用來作為索引的;
2,字串建立的索引按照字母順序排序;
3,如果使用LIKE,例項:

 SELECT * FROM userinfo WHERE realName LIKE '王%'; 

這種情況是可以使用索引的;但是LIKE '_王雷' 或者LIKE '%王雷'都是不能使用索引的;

7.使用非等值查詢的時候MySQL 無法使用Hash 索引

四、參考資料

五、小結

      彙總就是對sql語句、資料庫機構、硬體方面的優化,通過各種方法提高sql操作的速度,這個就是我們要整理的。