mysql之優化器、執行計划、簡單優化


mysql之優化器、執行計划、簡單優化

2018-12-12 15:11 煙雨樓人 閱讀(794) 評論(0) 編輯 收藏

引用連接:

https://blog.csdn.net/DrDanger/article/details/79092808
https://blog.csdn.net/wildpen/article/details/81335777

 

sql語句在sql層的流程:

用戶傳入sql-----查詢緩存(命中緩存可直接返回結果)----解析器(生成sql解析樹)----預處理器(可能sql等價改寫)-----查詢優化器(生成sql執行計划)----查詢執行引擎----結果返回給用戶。

1.優化器是什么?

優化器是數據庫的一個核心子系統,你也可以把他理解為MySQL數據庫中的一個核心模塊或者一個核心功能模塊。

2.優化器的目的:

優化器的目的是按照一定原則來得到她認為的目標SQL在當前情形下最有效的執行路徑,優化器的目的是為了得到目標SQL的執行計划.

3.優化器分類:

傳統關系型數據庫里面的優化器分為CBO和RBO兩種。

RBO--- Rule_Based Potimizer 基於規則的優化器:

RBO :RBO所用的判斷規則是一組內置的規則,這些規則是硬編碼在數據庫的編碼中的,RBO會根據這些規則去從SQL諸多的路徑中來選擇一條作為執行計划(比如在RBO里面,有這么一條規則:有索引使用索引。那么所有帶有索引的表在任何情況下都會走索引)所以,RBO現在被很多數據庫拋棄(oracle默認是CBO,但是仍然保留RBO代碼,MySQL只有CBO)

RBO最大問題在於硬編碼在數據庫里面的一系列固定規則,來決定執行計划。並沒有考慮目標SQL中所涉及的對象的實際數量,實際數據的分布情況,這樣一旦規則不適用於該SQL,那么很可能選出來的執行計划就不是最優執行計划了。

CBO---Cost_Based Potimizer 基於成本的優化器:

CBO :CBO在會從目標諸多的執行路徑中選擇一個成本最小的執行路徑來作為執行計划。這里的成本他實際代表了MySQL根據相關統計信息計算出來目標SQL對應的步驟的IO,CPU等消耗。也就是意味着數據庫里的成本實際上就是對於執行目標SQL所需要IO,CPU等資源的一個估計值。而成本值是根據索引,表,行的統計信息計算出來的。(計算過程比較復雜)

4.Cardinality的解釋:基數。

Cardinality是CBO特有的概念,它是指指定集合包含的記錄數,說白了就是指定結果集的行數。Cardinality和成本值的估計息息相關,因為MySQL的指定結果集所消耗的io資源可以近似看做隨着該結果集的遞增而遞增。
通過SHOW INDEX結果中的列Cardinality來觀察。

對於Cardinality總結:

1. 列值代表的是此列中存儲的唯一值的個數(如果此列為primary key 則值為記錄的行數)

2. 列值只是個估計值,並不准確。

3. 列值不會自動更新,需要通過Analyze table來更新一張表或者mysqlcheck -Aa來進行更新整個數據庫。

4. 列值的大小影響Join時是否選用這個Index的判斷。

5.Selectivity:可選擇率:

 可選擇率也是CBO特有的概念,它是指施加指定條件后返回結果集的記錄數占未施加任何謂詞條件的原始結果集的記錄數的比率(謂詞條件,可以理解為where等限定詞進行限定)
selectivity(可選擇率)=施加指定謂詞條件后返回結果集的記錄數/未施加指定謂詞條件的結果集的記錄數,我們可以通過可選擇率的估計,來確定一個列上是否需要添加索引,實際上,MySQL的CBO也是通過可選擇率來確定是否走索引,值越大在做表連接的時候,就越有機會選擇這個索引。

6.執行計划 

1. 查詢優化器使用統計信息為SQL選擇執行計划。

2. mysql沒有數據直方圖也無法手工刪除統計信息。

3. 在服務器層有查詢優化器,卻沒有保存數據和索引統計信息。統計信息由存儲引擎實現,不同的存儲引擎會存儲不同的統計信息。

4. 統計信息分為索引的統計信息,表的統計信息。

統計信息的收集:mysql> analyze table t1;

Analyze table收集表和索引統計信息,適用於MyISAM和InnoDB。對於INNODB存儲引擎,在以下情況下,會重新收集統計信息。

1. 表第一次打開的時候。

2. 表修改的行超過1/16 或者新插入20億行的時候計算索引的統計信息。

3. 執行show index或者查詢information schema下的表。

7.統計信息的查看:

索引統計信息:

Show index from table或information_schema.statistics表

表統計信息:

Show table status like或information_schema.tables表

8.查看執行計划

mysql> explain select * from employees.employees limit 1\G;

*************************** 1. row ***************************

           id: 1

  select_type: SIMPLE  查詢類型

        table: employees  當前查詢被掃描到的表  可能是表的別名

   partitions: NULL         

         type: ALL       表掃描方式

possible_keys: NULL      可能被使用到的索引

          key: NULL      被使用的索引

      key_len: NULL      使用過的索引的長度

          ref: NULL      

         rows: 299246   掃描的數據的行數(examin)

     filtered: 100.00   (返回比例)

        Extra: NULL      額外的說明

id列:一般情況下如果不帶子查詢,id不增加.如果有子查詢的,自查詢數字增加,先執行子查詢,即數據大的先執行.(從大到小).

select_type列:simple 簡單查詢(不帶子查詢)       PRIMARY 子查詢最外層查詢

SUBQUERY(不在 from后面子查詢)     DEPENDENT SUBQUERY

DERIVED(在 from子句中的子查詢)      union:在union關鍵字的后的select語句

union result:采用匿名臨時進行檢索結果

table列:這列表示正在訪問某個表

type:查詢類型: 查詢效率依次下降.     (system---const---eq_ref---ref---fulltext---ref_of_null----index_merge---unique_subquery---index_subquery---range---index----all)

ALL ----全表掃描    index----按照索引的次序進行全表掃描     range-----索引范圍掃描

ref-----索引直接獲取單個數據  const----- -通過主鍵直接返回數據

執行計划列:

id列:用於表示select的列,如果都是簡單查詢則都是1,如果有子查詢,則會進行增加,數據大的先執行。

查看執行計划的規則:(最大最上規則)

1.id相同,執行順序由上至下

2.id不同,如果是子查詢,id序號會遞增,id值越大優先級越高,越先被執行。

3.id既有相同的,又有不同的。id如果相同認為是一組,執行順序由上至下; 在所有組中,id值越大優先級越高,越先執行。

possible_keys列:可能會使用那些索引

key列:使用到的索引

Key_len列:索引長度列.可根據索引的使用長度來判斷復合索引的使用情況.

Ref列:索引中查找到的值所用的列或者常量

rows列:MySQL需要掃描的行數

table列:這列表示正在訪問某個表.

Select type 列的值有:

   1.PRIMARY:查詢中包含任何復雜的子部分,最外層的查詢

  2.SUBQUERY:SELECT或WHERE中包含的子查詢部分(不是from后的子查詢).

  3.DERIVED:在FROM中包含的子查詢被標記為DERIVER(衍生), MySQL會遞歸執行這些子查詢,把結果放到臨時表中。

  4.UNION:若第二個SELECT出現UNION,則被標記為UNION, 若UNION包含在FROM子句的子查詢中,外層子查詢將被標記為DERIVED。

  5.UNION RESULT:從UNION表獲取結果的SELECT

  6.simple:簡單查詢,不包括子查詢和union.

type列:查詢類型:(12種)

system---const---eq_ref---ref---fulltext---ref_of_null----index_merge---unique_subquery---index_subquery---range---index----all        

說明:按照此順序查詢效率依次下降。  (subquery:子查詢)

Extra列的值:

  1.using filesort需要使用額外的排序得到結果(進行內存排序或者硬盤排序)

  2.using index 優化器只需要使用索引就可以返回結果(覆蓋索引)

  3.using index condition 優化器使用index condition pushdown優化

  4.using join buffer 優化器需要在使用join buffer 

  5.using mrr 優化器使用mrr優化

  6.using tpporary 優化器需要使用臨時表

  7.using where 優化器使用where過濾

 

 9.type列字段的詳細說明:
一般來說,得保證查詢至少達到range級別,最好能達到ref。

system:表只有一行記錄(等於系統表),這是const類型的特例,平時不會出現

const:如果通過索引依次就找到了,const用於比較主鍵索引或者unique索引。 因為只能匹配一行數據,所以很快。如果將主鍵置於where列表中,MySQL就能將該查詢轉換為一個常量

eq_ref:唯一性索引掃描,對於每個索引鍵,表中只有一條記錄與之匹配。常見於主鍵或唯一索引掃描

ref:非唯一性索引掃描,索引直接獲取單個數據,返回匹配某個單獨值的所有行。本質上也是一種索引訪問,它返回所有匹配 某個單獨值的行,然而它可能會找到多個符合條件的行,所以它應該屬於查找和掃描的混合體

range:只檢索給定范圍的行,使用一個索引來選擇行。key列顯示使用了哪個索引,一般就是在你的where語句中出現between、<</font>、>、in等的查詢,這種范圍掃描索引比全表掃描要好,因為只需要開始於縮印的某一點,而結束於另一點,不用掃描全部索引

index:Full Index Scan ,index與ALL的區別為index類型只遍歷索引樹,這通常比ALL快,因為索引文件通常比數據文件小。 (也就是說雖然ALL和index都是讀全表, 但index是從索引中讀取的,而ALL是從硬盤讀取的)

all:Full Table Scan,遍歷全表獲得匹配的行

 
10. Extra列值詳解
 1、Using filesort: 說明MySQL會對數據使用一個外部的索引排序,而不是按照表內的索引順序進行讀取。MySQL中無法利用索引完成的排序操作稱為“文件排序”

2、Using temporary:  使用了臨時表保存中間結果,MySQL在對查詢結果排序時使用臨時表。常見於排序order by和分組查詢group by

3、Using index: 表示相應的SELECT操作中使用了覆蓋索引(Covering Index),避免訪問了表的數據行,效率不錯。 如果同時出現using where,表明索引被用來執行索引鍵值的查找; 如果沒有同時出現using where,表明索引用來讀取數據而非執行查找動作 覆蓋索引(Covering Index): 理解方式1:SELECT的數據列只需要從索引中就能讀取到,不需要讀取數據行,MySQL可以利用索引返回SELECT列表中 的字段,而不必根據索引再次讀取數據文件,換句話說查詢列要被所建的索引覆蓋 理解方式2:索引是高效找到行的一個方法,但是一般數據庫也能使用索引找到一個列的數據,因此他不必讀取整個行。 畢竟索引葉子節點存儲了他們索引的數據;當能通過讀取索引就可以得到想要的數據,那就不需要讀取行了,一個索引 包含了(覆蓋)滿足查詢結果的數據就叫做覆蓋索引 注意: 如果要使用覆蓋索引,一定要注意SELECT列表中只取出需要的列,不可SELECT *, 因為如果所有字段一起做索引會導致索引文件過大查詢性能下降

6、impossible where: WHERE子句的值總是false,不能用來獲取任何元組

7、select tables optimized away: 在沒有GROUP BY子句的情況下基於索引優化MIN/MAX操作或者對於MyISAM存儲引擎優化COUNT(*)操作, 不必等到執行階段再進行計算,查詢執行計划生成的階段即完成優化

8、distinct: 優化distinct操作,在找到第一匹配的元祖后即停止找同樣值的操作

 
當order by 和 group by無法使用索引時,增大max_length_for_sort_data參數設置和增大sort_buffer_size參數的設置。
 
11.優化示例
 1.更改INSERT INTO為 INSERT DELAYED INTO

更改INSERT INTO為 INSERT DELAYED INTO,是客戶端提交數據給MySQL,MySQL返回OK狀態給客戶端。而這是並不是已經將數據插入表,而是存儲在內存里面等待排隊。當mysql有空余時,再插入。

這樣的好處是,提高插入的速度,客戶端不需要等待太長時間。壞處是,不能返回自動遞增的ID,以及系統崩潰時,MySQL還沒有來得及插入數據的話,這些數據將會丟失。

通過phpMyAdmin觀測MySQL的進程,提交后,會有一些用戶為DELAYED,狀態為Waiting for INSERT的進程。過一會,數據完全插入后就消失了。

2.優化group by語句

由group by id 改為 group by id order by null

默認情況下,MySQL 對所有 GROUP BY col1,col2….的字段進行排序。這與在查詢中指定 ORDER BY col1,col2…類似。因此,如果顯式包括一個包含相同的列的 ORDER BY 子句,則對 MySQL 的實際執行性能沒有什么影響。 如果查詢包括 GROUP BY 但用戶想要避免排序結果的消耗,則可以指定 ORDER BY NULL禁止排序。

3.優化order by 語句

MySQL 可以使用一個索引來滿足 ORDER BY 子句,而不需要額外的排序。 WHERE 條件和 ORDER BY 使用相同的索引,並且 ORDER BY 的順序和索引順序相同,並且 ORDER BY 的字段都是升序或者都是降序。

以下情況不使用索引:

1.order by 字段混合desc和asc.  例如:order by t1 desc, t2 asc;

2.用於查詢行的關鍵字與order by中使用的不同。例如:...where t1=a order by t2;

3.對不同的關鍵字使用order by. 例如:order by t1,t2;

4.優化嵌套查詢

使用子查詢消除方法。(將子查詢改為join連接)

Select * from t1 where id not in (select id from t2)改為:

Select * from t1 left join t2 on t1.id=t2.id where t2.id is null;

5.優化or條件

對於含有 OR 的查詢子句,如果要利用索引,則 OR 之間的每個條件列都必須用到索引; 如果沒有索引,則應該考慮增加索引。  

6.一些sql提示

1.use index  ---指定索引

在查詢語句中表名的后面,添加 USE INDEX 來提供希望 MySQL 去參考的索引列表,就可 以讓 MySQL 不再考慮其他可用的索引。 

Select * from lbghaha where d=’e’;

mysql> select * from lbghaha use index(ind_haha) where d='e'; ---指定索引執行

2.IGNORE INDEX     --忽略索引

如果用戶只是單純地想讓 MySQL 忽略一個或者多個索引,則可以使用 IGNORE INDEX 作為 HINT(暗示)。

mysql> select * from lbghaha ignore index(ind_haha) where d='e';

3.FORCE INDEX

為強制 MySQL 使用一個特定的索引,可在查詢中使用 FORCE INDEX 作為 HINT(暗示)。例如, 當不強制使用索引的時候,因為 id 的值都是大於 0 的,因此 MySQL 會默認進行全表掃描, 而不使用索引。

mysql> select * from lbghaha force index(ind_haha) where d='e';

 12.大數據量數據入庫應該如何優化

對於數據庫層面如何優化:

優化參數:

1.重建索引

2.trx_commit sync_bin sql_log_bin 和IO有關參數

3.批量提交SQL ,不要 insert commit; insert commit; 而是 for insert(10000) commit;

優化硬件:

OLTP環境,可以購買更好的硬盤來支撐業務,更好的網卡來支撐流量

 

業務優化

1.多線程處理,使用多線程同時讀寫數據

2.設置隊列,做大量數據的緩沖工作(隊列  內存緩存(redis memcache))

 

架構優化

1.使用一些mysql的特定的存儲引擎來支撐大壓力的數據入庫

2.或者不用mysql,用一些nosql或者hadoop來支撐

 

大批量數據入庫方法:

對於 MyISAM 存儲引擎的表:

當用 load 命令導入數據的時候,適當的設置可以提高導入的速度。 (對innodb無效)

Alter table lbg disable keys;      ---取消索引

Loading the data               ---導入數據

Alter table lbg enable keys;       --索引生效

Innodb儲存引擎導入大批量數據快速的方法:

(1)因為 InnoDB 類型的表是按照主鍵的順序保存的,所以將導入的數據按照主鍵的順 序排列,可以有效地提高導入數據的效率。

(2)在導入數據前執行 SET UNIQUE_CHECKS=0,關閉唯一性校驗,在導入結束后執行 SET UNIQUE_CHECKS=1,恢復唯一性校驗,可以提高導入的效率。

(3)如果應用使用自動提交的方式,建議在導入前執行 SET AUTOCOMMIT=0,關閉自

動提交,導入結束后再執行 SET AUTOCOMMIT=1,打開自動提交,也可以提高導入的效率。

 

參考網址見:https://www.cnblogs.com/lbg-database/p/10108513.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM