MySQL索引及查詢優化
1. MySQL索引的原理
1.1 索引目的
索引的目的在于提高查詢效率,可以類比字典,如果要查“mysql”這個單詞,我們肯定需要定位到m字母,然后從下往下找到y字母,再找到剩下的sql。如果沒有索引,那么你可能需要把所有單詞看一遍才能找到你想要的,如果我想找到m開頭的單詞呢?或者ze開頭的單詞呢?
1.2 索引原理
除了詞典,生活中隨處可見索引的例子,如火車站的車次表、圖書的目錄等。它們的原理都是一樣的,通過不斷的縮小想要獲得數據的范圍來篩選出最終想要的結果,同時把隨機的事件變成順序的事件,也就是我們總是通過同一種查找方式來鎖定數據。
每個數據表都有一個主鍵(如果沒有主鍵,數據庫會將該表中的唯一索引當作主鍵使用?),MySQL會以主鍵的方式構造一棵樹,葉子節點存放該主鍵對應的整行數據。
自己建立的索引,一般叫做輔助索引,輔助索引的樹,也自己節點存放了兩個東西,一個是索引自身的值,另外一個是索引對應主鍵的值。
如果索引是聯合索引,比如UserID和AddTime索引的方式,索引葉子節點會存儲UserID和AddTime之間的配對+主鍵的配對數據。
1.3 索引的類型
1.3.1 B-樹索引
B-樹索引在生產環境更為廣泛,這里我只針對B-樹索引進行討論
B-樹索引是一個復雜的內容,可以參見B-tree。
1.3.2 Hash索引
哈希索引(Hash Index)建立在哈希表的基礎上,它只對使用了索引中的每一列的精確查找有用。對于每一行,存儲引擎計算出了被索引的哈希碼(Hash Code),它是一個較小的值,并且有可能和其他行的哈希碼不同。它把哈希碼保存在索引中,并且保存了一個指向哈希表中的每一行的指針。
在mysql中,只有memory存儲引擎支持顯式的哈希索引。
- Hash 索引僅僅能滿足"=","IN"和"<=>"查詢,不能使用范圍查詢。
- Hash 索引無法被用來避免數據的排序操作。
- Hash 索引不能利用部分索引鍵查詢。
- Hash 索引在任何時候都不能避免表掃描。
- Hash 索引遇到大量Hash值相等的情況后性能并不一定就會比B-Tree索引高 </ul>
- 使用UserID作為索引 </ul>
- Order by 的字段并不是索引
-
使用了兩種排序方向,但是索引都是使用升序排列的
在索引的原理里我們講到過如果索引是聯合索引,比如UserID和AddTime索引的方式,索引葉子節點會存儲UserID和AddTime之間的配對+主鍵的配對數據。
</blockquote>這種情況下,索引會以UserID進行排序,當UserID相同時再以AddTime進行排序(默認為升序),以我們現有的數據庫為例:
select UserID,ArriveDate from TestSQL where UserID in(2,3,4) order by UserID asc,ArriveDate desc;
這種情況將只使用UserID 作為索引,而ArriveDate 將不再作為索引。
</li>不符合最左(Left most)的條件
select * from TestSQL where ArriveDate = '2015-09-23 00:00:00' order by UserID desc
這種情況將不再使用UserID_ArriveDate索引
</li>聯合查詢的情況下,如果第一個篩選條件是范圍查詢,MySQL不再使用剩下的索引
select * from TestSQL where UserID >1 and UserID < 5 order by UserID desc,ArriveDate desc
這種情況下,因為UserID 為范圍查詢,所以就不會再使用ArriveDate 索引了。
</li> </ul>2.4 join 中使用索引
我們來新建一個TestSQL_join表,其結構與TestSQL 相同,但只有Mobile索引。首先來看一個簡單的join操作
select * from TestSQL join TestSQL_join on TestSQL.id = TestSQL.id
MySQL首先比較TestSQL和 TestSQL_join表那個行數少,如TestSQL中的記錄較少,TestSQL就是一個小表,而TestSQL_join則是大表,MySQL引 擎先把TestSQL中的ID全部去出來,然后根據id到TestSQL_join中查詢相關的記錄。
在這里,TestSQL中行數決定了循環的次數,但是TestSQL_join則決定了每次循環查詢所需要查詢的時間;這時如果TestSQL_join中的ID是索引則會大大減少查詢時間如下SQL:
select * from TestSQL a join TestSQL_join b on a.ID = b.ID
由于b.ID 是TestSQL_join的主鍵,查詢使用了TestSQL_join主鍵索引。
如果此時對TestSQL增加條件篩選:select * from TestSQL a join TestSQL_join b on a.ID = b.ID where a.UserID = 1;
因為a.UserID 是TestSQL的索引,所以在過濾TestSQL表的行數時,采用次索引查詢對應ID,然后根據ID查詢TestSQL_join的記錄。
如果此時針對TestSQL_join 增加where條件過濾:select * from TestSQL a join TestSQL_join b on a.ID = b.ID where a.UserID >1 and b.Mobile = "2147483647"
這種時候,因為TestSQL 和 TestSQL_join 根據where條件所篩選出來的行數大小可能會有變化,也就是說TestSQL_join 有可能會變成小表,這時候將會優先從TestSQL_join 查詢出相關ID,然后根據ID去查詢TestSQL。
join操作時,大表小表的概念,主要是按照兩張表分別執行對應查詢條件,哪個開銷更小,哪個就是小表。
join操作雖然在SQL層面很方便,而且在線上大流量的情況下,一旦SQL的join操作導致查詢緩慢,較難即使優化。另外在服務化的系統中,容易導致業務領域不清晰,所以在互聯網大流量的應用中是不推薦使用join操作的。
3. 索引建立的原則
-
使用區分度高的列作為索引
</li>
區分度的公式是count(distinct col)/count(*),表示字段不重復的比例,區分度越高,索引樹的分叉也就越多,一次性找到的概率也就越高。 -
盡量使用字段長度小的列作為索引
</li> -
使用數據類型簡單的列(int 型,固定長度)
</li> -
選用NOT NULL的列
</li>
在MySQL中,含有空值的列很難進行查詢優化,因為它們使得索引、索引的統計信息以及比較運算更加復雜。你應該用0、一個特殊的值或者一個空串代替空值。 -
盡量的擴展索引,不要新建索引。比如表中已經有a的索引,現在要加(a,b)的索引,那么只需要修改原來的索引即可。這樣也可避免索引重復。
4. 索引使用的原則
</li> -
最左前綴匹配原則(leftmost),mysql會一直向右匹配直到遇到范圍查詢(>、<、between、like)就停 止匹配,=和in可以亂序,一個聯合索引中,如UserID,ArriveDate的聯合索引,使用ArriveDate in ()and UserID = 的任意順序,MySQL的查詢優化器會幫你優化成索引可以識別的形式
</li> -
索引列不能參與計算
select * from TestSQL where UserID + 1 >1 and UserID < 5
這種方式UserID 的索引就不會再被使用,因為在進行檢索時,需要把所有元素都應用函數才能比較,顯然成本太大。
</li>
另外當使用<>,like通配符放置在最前面 如:like'%ddd' ,not, in, !=等運算符都不會使用索引。 -
查詢數據庫記錄時,查詢到的條目數盡量小,當通過索引獲取到的數據庫記錄> 數據庫總揭露的1/3時,SQL將有可能直接全表掃描,索引就失去了應有的作用。
</li> </ul>5. explain的使用
explain是MySQL查詢優化過程的神器,詳情可以查看explain的使用
References
High Performance MySQL-THIRD EDITION
</blockquote> 來自:http://blog.brucefeng.info/post/mysql-index-query
explain的使用
MySQL索引學習漫畫
MySQL索引原理及慢查詢優化
理解MySQL——索引與優化
mysql索引的類型和優缺點
本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!相關資訊
sesese色
1.3.3 空間索引(R-樹)索引
主要用于GIS中空間數據的存儲,但是MySQL的空間索引支持并不好,現在多使用PostgreSQL。
1.3.4 全文索引(Full-text)索引
文本字段上的普通索引只能加快對出現在字段內容最前面的字符串(也就是字段內容開頭的字符)進行檢索操作。如果字段里存放的是由幾個、甚至是多個單 詞構成 的較大段文字,普通索引就沒什么作用了。這種檢索往往以LIKE %word%的形式出現,這對MySQL來說很復雜,如果需要處理的數據量很大,響應時間就會很長。
這類場合正是全文索引(full-text index)可以大顯身手的地方。在生成這種類型的索引時,MySQL將把在文本中出現的所有單詞創建為一份清單,查詢操作將根據這份清單去檢索有關的數 據記錄。全文索引即可以隨數據表一同創建,也可以等日后有必要時再使用下面這條命令添加:
ALTER TABLE tablename ADD FULLTEXT(column1, column2)
2. 索引的合理使用
字段名 | 數據類型 | NULL | INDEX | </tr> </tbody>||||||
---|---|---|---|---|---|---|---|---|---|
ID | int(10) | NOT NULL | pk | </tr>||||||
UserID | int(10) | NOT NULL | |||||||
Mobile | varchar(15) | NOT NULL | |||||||
ArriveDate | DateTime | NOT NULL | |||||||
AddDate | DateTime | NOT NULL | |||||||
UpdateTime | timetamp | NOT NULL |
UserID | ArriveDate | </tr> </tbody>
---|---|
2 | 2015-09-23 00:00:00 | </tr>
12 | 2015-09-23 00:00:00 | </tr> </tbody> </table>