天天看點

MySQL學習筆記 —— 索引結構與優化(三)索引的設計與優化

        不管對MySQL學習打算淺嘗即止,還是打算深入研究,相信都是對這部分内容最感興趣的。這是與應用設計、開發與實施有着最直接關系的一個子產品。

        這裡說個題外話,在很多公司裡,我們開發往往是無法掌權資料庫管理的生殺大權的,索引的設計與建立一般掌控在項目經理和dba手中(稍微大點的團隊中新手開發人員一般人數不少,暴露權限确實可能會建的亂七八糟)。不過,事實上開發參加索引的建立是能讓索引更加符合邏輯的,畢竟一般情況下開發才是離代碼邏輯、sql語句設計距離最近的人。

        是以對于索引的建立、sql的優化,是開發人員必不可少的技能,一般我們設計索引、優化sql按照下面這個思維順序進行:

        1、盡量使用主鍵查詢

        上一篇B+樹索引的結構可以直覺的發現,主鍵查詢是可以避免回表的,一次查詢搞定,速度肯定最快。所謂回表,說的是輔助索引查詢時,一般是在輔助索引B+樹找到該資料的關聯主鍵,然後再在聚集索引樹上查找對應資料,這将導緻多了将近一倍的IO次數。 

        2、考慮使用覆寫索引

        覆寫查詢同樣也是避免回表的一種操作,因為需要的資料列在輔助索引樹上都能取到,是以在輔助索引樹得到目标行後,不需要回到聚集索引樹查詢。像一般管理系統的分頁查詢一般是展示使用者所需的摘要資訊清單,一般這裡可以考慮将所有被查詢列和查詢條件設計為聯合索引。提速效果和主鍵查詢一緻。但是聯合索引并非沒有限制,我們可以先來看看簡單的聯合索引結構:

MySQL學習筆記 —— 索引結構與優化(三)索引的設計與優化

        可以看到聯合索引樹的鍵值是所有索引列組成的,是以要注意避免總占用位元組數過多,導緻樹深度太大。第二個要注意的則是聯合索引的最左比對原則。

        3、注意聯合索引的最左比對原則

        根據聯合索引樹可以看到,B+樹是根據最左邊的索引來做大方向上的B+樹的key建構的:根據最左邊的列排序,最左列值一緻的根據第二個列排序,依此類推。是以查詢條件一定要帶上最左邊的列,聯合索引才會被使用。

        4、開啟索引下推

        索引下推是MySQL5.6添加的,用于優化資料查詢。預設是開啟狀态的,我們要做的隻是不要手賤去關閉它。下面是在“初識MySQL”篇MySQL的結構圖簡略版,我們簡單的回顧下來了解索引下推的原理:

MySQL學習筆記 —— 索引結構與優化(三)索引的設計與優化

         sql查詢時,請求從用戶端=>背景服務層=>存儲引擎,存儲引擎層将資料傳回到server層,然後到用戶端。在沒有索引下推的時候,使用聯合索引的列做條件查詢時,根據最左原則是隻會用最左邊的索引查詢出資料的聚集索引,然後回表掃描整行資料傳回給服務server層,最後再根據其它條件篩選。而使用了索引下推之後,會直接根據索引的資訊在聯合索引樹中篩選好資料傳回。減少了大量的回表的次數,進而提升效率。

        5、索引列的選擇

        對于索引列的選擇,除了上篇博文提到的,過大的列不會被考慮在内外,值範圍太小的列也是不會被考慮建立索引的,比方說:sex性别,一般情況下,系統的性别是隻有男/女兩個取值的,聯想一下B+樹的結構也能想的出來,這種索引建了是沒什麼意義的(還會讓資料庫多元護一個沒有意義的B+樹)。當你無法預估一個列的值範圍時,可以使用指令show index from ‘表名’顯示的cardnality列來計算,當cardnality÷行數的值越接近1,值範圍便越大。

        6、使用查詢計劃觀察、調整(索引失效問題)

        索引并不是說建立了,sql語句上用到了,真實查詢的時候就會使用的。MySQL有一個叫做優化器的東西來主動判斷是否使用哪些索引:比方說,如果有更小的輔助索引出現,執行select count(*) from ‘表明’時,如果有更小的輔助索引,優化器可能不會去使用聚集索引來做統計操作。

        是以,判斷一個sql的好壞不能全憑主觀的臆想推論,需要借助查詢計劃來用資料來說話。一般想使用查詢計劃可以在select語句前加上explain關鍵字,或者在用戶端如navicat上右鍵sql語句,點選查詢計劃檢視。下面來看看查詢字段各列的含義:

MySQL學習筆記 —— 索引結構與優化(三)索引的設計與優化

id:select查詢的序列号,包含三種情況 

1、id相同時,從上往下執行;

2、id不同時,值越大優先級越高;

3、相同不同都有時,值越大優先級越高,相同id的語句從上往下執行

select_type:查詢的類型,主要是用于區分普通查詢、聯合查詢、子查詢等複雜的查詢

SIMPLE 簡單的select查詢,查詢中不包含子查詢或者union 

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

SUBQUERY 在select 或 where清單中包含了子查詢 

DERIVED 在from清單中包含的子查詢被标記為derived(衍生),mysql或遞歸執行這些子查詢,把結果放在臨時表裡 

UNION select出現在union之後,則被标記為union;union包含在from子句的子查詢中,union前面的select由primary變成drived 

UNION RESULT 從union表擷取結果的select 

 table:表名,當有别名時顯示别名

partitions:查詢将用到的分區,非分區表顯示null

type:通路類型,sql查詢優化中一個很重要的名額

結果值從好到壞依次是:system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL;一般來說,好的sql查詢至少達到range級别,最好能達到ref,對每個類型的具體解釋看官網:https://dev.mysql.com/doc/refman/5.7/en/explain-output.html#explain-join-types這個真的要看看(過一遍),比較重要,官網上有枚舉說明和sql對應的案例

possible_keys:可能用到的索引

keys:實際上用到的索引,查詢優化器會判斷有沒有需要使用索引的必要

key_len:索引中使用的位元組數

查詢中使用的索引的長度(最大可能長度),并非實際使用長度,理論上長度越短越好。長度是根據表定義計算而得的,不是通過表内檢索出的

ref:與索引比較的列

rows:要查詢的行數的預估值

filtered:按表條件過濾的行百分比

extra:附加資訊

這也是很重要的一列資訊,查詢是否很慢往往可以參考這裡,常出現的有:

Using filesort 無法利用索引完成排序,将查詢結果是哦那個“檔案排序” 

Using temporary 使用臨時表儲存中間結果,也就是說mysql在對查詢結果排序時使用了臨時表,常見于order by 和 group by 

Using index 表示相應的select操作中使用了覆寫索引(Covering Index),避免了通路表的資料行,效率高 

如果同時出現Using where,表明索引被用來執行索引鍵值的查找

如果沒用同時出現Using where,表明索引用來讀取資料而非執行查找動作

Using where 使用了where過濾

Using join buffer (Block Nested Loop),Using join buffer (Batched Key Access) 來自早期連接配接的表被分部分讀入連接配接緩沖區,然後它們的行從緩沖區中用于執行與目前表的連接配接

Impossible WHERE where子句的值總是false,不能用來擷取任何元祖

distinct 優化distinct操作,在找到第一個比對的元祖後即停止找同樣值得動作

        具體出現的各個情況及含義可以檢視官網:https://dev.mysql.com/doc/refman/5.7/en/explain-output.html#explain-extra-information

        7、優秀的sql結構

        很顯然優秀的sql結構是對多表查詢來說的,單表查詢直來直去,不存在什麼優秀的sql結構。多表查詢分為連接配接查詢和子查詢兩種方式,所謂連接配接查詢,本質上就是在多個表的笛卡爾積裡面查詢資料;而子查詢是将内層的查詢結果當作外層查詢的參數,本質上也可以當作多個表的笛卡爾積裡查詢結果。

        子查詢分為select/where後跟着的select和from後面跟着的select;連接配接查詢分為内連接配接inner join、左/右連接配接left join和全連接配接(union配合left join實作)查詢。這意味着一個查詢邏輯的實作可以有多種方法。而不同的方法孰優孰劣是由優化器層層算法決定的,不同的資料量和表設計下,千萬不能迷信大部分博文說的那樣哪種查詢一定比另一種方式的效率高(我看過一些确實連接配接查詢效率高的機率大一些,但是不同的業務案例是無法放在一起比較的,就像你無法用你的城市的平均工資去衡量另一個城市的平均工資一樣)。若要真正比較,務必還是要檢視執行計劃來觀察比較。

        下面我麼們建兩張字段相同含有id(primaryKey), name(index), age的表,各自插入10萬測試資料,看看不同方式的關聯查詢效率有什麼差別:

1、普通連接配接查詢

MySQL學習筆記 —— 索引結構與優化(三)索引的設計與優化

 2、内連接配接查詢

MySQL學習筆記 —— 索引結構與優化(三)索引的設計與優化

 3、左連接配接查詢

MySQL學習筆記 —— 索引結構與優化(三)索引的設計與優化

 4、全連接配接查詢(僞全連接配接,由left join和union合作完成)

MySQL學習筆記 —— 索引結構與優化(三)索引的設計與優化

 5、子查詢(在where)

MySQL學習筆記 —— 索引結構與優化(三)索引的設計與優化

6、子查詢(在select) 

MySQL學習筆記 —— 索引結構與優化(三)索引的設計與優化

7、子查詢(在from)+全連接配接,這個故意寫的複雜些 

MySQL學習筆記 —— 索引結構與優化(三)索引的設計與優化

        可以看到,上面查詢的結果基本上是一緻的,此中除了4、全連接配接查詢和7、故意寫複雜的查詢外,其它效率基本是一緻的。甚至符合業務的情況下5、子查詢由于id=1的查詢type為const,效率最高。這裡并不是要給各種查詢效率做排序,隻是證明不要迷信網絡所說的某某查詢效率一定快,不同的業務要求下、不同的資料量下,個終查詢效率的快慢并非是有固定排名的,要比較的話一定要自己觀察查詢計劃來調整、優化。

如有錯誤,敬請斧正;歡迎轉載,但請務必注明出處;最後,在此向神奇的海螺保證,絕不太監!!!