MySQL的基本存儲結構是頁(記錄都存在頁裏邊): html
因此說,若是咱們寫select * from user where indexname = 'xxx'這樣沒有進行任何優化的sql語句,默認會這樣作:面試
- 定位到記錄所在的頁:須要遍歷雙向鏈表,找到所在的頁
- 從所在的頁內中查找相應的記錄:因爲不是根據主鍵查詢,只能遍歷所在頁的單鏈表了
將無序的數據變成有序的數據(就像查目錄同樣)。 算法
要找到id爲8的記錄簡要步驟:
很明顯的是:沒有用索引咱們是須要遍歷雙向鏈表來定位對應的頁,如今經過 「目錄」 就能夠很快地定位到對應的頁上了!(二分查找,時間複雜度近似爲O(logn))sql
其實底層結構就是B+樹,B+樹做爲樹的一種實現,可以讓咱們很快地查找出對應的記錄。數據庫
對於哈希索引來講,底層的數據結構就是哈希表,所以在絕大多數需求爲單條記錄查詢的時候,能夠選擇哈希索引,查詢性能最快;其他大部分場景,建議選擇BTree索引。bash
MySQL的BTree索引使用的是B樹中的B+Tree。但對於主要的兩種存儲引擎(MyIsam和InnoDB)的實現方式是不同的。服務器
ps:聯合索引的特色:遵循最左前綴的規則數據結構
MySQL中的索引能夠以必定順序引用多列,這種索引叫做聯合索引。如User表的name和city加聯合索引就是(name,city),而最左前綴原則指的是,若是查詢的時候查詢條件精確匹配索引的左邊連續一列或幾列,則此列就能夠被用到。以下:函數
select * from user where name=xx and city=xx ; //能夠命中索引
select * from user where name=xx ; // 能夠命中索引
select * from user where city=xx ; // 沒法命中索引
複製代碼
這裏須要注意的是,查詢的時候若是兩個條件都用上了,可是順序不一樣,如 city= xx and name =xx,那麼如今的查詢引擎會自動優化爲匹配聯合索引的順序,這樣是可以命中索引的。post
因爲最左前綴原則,在建立聯合索引時,索引字段的順序須要考慮字段值去重以後的個數,較多的放前面。ORDER BY子句也遵循此規則。
B+Tree葉子節點的data域存放的是數據記錄的地址。在索引檢索的時候,首先按照B+Tree搜索算法搜索索引,若是指定的Key存在,則取出其data域的值,而後data域的值爲地址讀取響應的數據記錄。這被稱爲「非聚簇索引」。
其數據文件自己就是索引文件。MyIsam中,索引文件和數據文件是分離的,而InnoDB中,其表數據文件自己就是按B+Tree組織的一個索引結構,樹的葉節點data域保存了完整的數據記錄。這個索引的key是數據表的主鍵,所以InnoDB表數據文件自己就是主索引。這被稱爲「聚簇索引(或彙集索引)」,而其他的索引都做爲輔助索引,輔助索引的data域存儲相應記錄主鍵的值而不是地址,這也是和MyIsam不一樣的地方。在根據主索引搜索時,直接找到key所在的節點便可取出數據;在根據輔助索引搜索時,則須要先取出主鍵的值,再走一遍主索引。所以,在設計表的時候,不建議使用過長的字段做爲主鍵,也不建議使用非單調的字段做爲主鍵,這會形成主索引頻繁分裂。
【注】更詳細的請出門左轉: MySQL索引和SQL調優
若是一個索引包含(或者說覆蓋)全部須要查詢的字段的值,咱們就稱之爲「覆蓋索引」。咱們知道在InnoDB存儲引擎中,若是不是主鍵索引,葉子節點存儲的是主鍵+列值。最終仍是要「回表」,也就是要經過主鍵再查找一次,這樣就會比較慢。覆蓋索引就是要查詢的列和索引是對應的,不作回表操做。
加入建立了索引(username,age),在查詢數據的時候:
select username,age from user where username = ‘Java’ and age = 22;
要查詢成的列在葉子節點都存在,因此不用回表。
select * from tb1 where name like '%cn';
複製代碼
以'%'開頭:索引失效。
以'%'結尾:索引能夠用。
select * from tb1 where reverse(name) = 'wupeiqi';
複製代碼
select * from tb1 where nid = 1 or email = 'seven@live.com';
複製代碼
特別地:當or條件中有未創建因此的列才失效,如下會走索引
select * from tb1 where nid = 1 or name = 'seven';
select * from tb1 where nid = 1 or email = 'seven@live.com' and name = 'alex'
複製代碼
若是條件中有or,即便其中有條件帶索引也不會使用(這也是爲何儘可能少於or的緣由)
注意:要想使用or,又想讓索引生效,只能將or條件中的每一個列中都加上索引.
類型不一致 若是列類型是字符串,那必定要在條件中將數據使用引號引用起來,不然不使用索引
含有'!=' 或者'>'
select * from tb1 where name != 'alex'
複製代碼
【特別地】:若是是主鍵或索引整數類型,則仍是會走索引
select * from tb1 where nid > 123
select * from tb1 where num > 123
複製代碼
select email from tb1 order by name desc;
複製代碼
當根據索引排序時,選擇的映射若是不是索引,則不走索引
【特別地】:若是對主鍵排序,則仍是會走索引:
select * from tb1 order by nid desc;
複製代碼
若是組合索引爲:(name, email)
- name and email # 使用索引
- name # 使用索引
- email # 不使用索引
複製代碼
注:以上內容整理自: