MySQL索引小結

1 MySQL的基本存儲結構

MySQL的基本存儲結構是頁(記錄都存在頁裏邊): html

  • 各個數據頁能夠組成一個雙向鏈表
  • 每一個數據頁中的記錄又能夠組成一個單向鏈表
    • 每一個數據頁都會爲存儲在它裏邊兒的記錄生成一個頁目錄,在經過主鍵查找某條記錄的時候能夠在頁目錄中使用二分法快速定位到對應的槽,而後再遍歷該槽對應分組中的記錄便可快速找到指定的記錄
    • 以其餘列(非主鍵)做爲搜索條件:只能從最小記錄開始依次遍歷單鏈表中的每條記錄。

因此說,若是咱們寫select * from user where indexname = 'xxx'這樣沒有進行任何優化的sql語句,默認會這樣作:面試

  • 定位到記錄所在的頁:須要遍歷雙向鏈表,找到所在的頁
  • 從所在的頁內中查找相應的記錄:因爲不是根據主鍵查詢,只能遍歷所在頁的單鏈表了

2 爲何要使用索引?

  • 經過建立惟一性索引,能夠保證數據庫表中每一行數據的惟一性。
  • 能夠大大加快數據的檢索速度(大大減小檢索的數據量),這也是建立索引的最主要緣由。
  • 幫助服務器避免排序和臨時表。
  • 將隨機IO變爲順序IO。
  • 能夠加速表與表之間的鏈接,特別是在實現數據的參考完整性方面特別有意義。

3 索引這麼多優勢,爲何不對錶中的每個列建立一個索引呢?

  • 當表中的數據進行增長、刪除和修改的時候,索引也要動態的維護,這樣會下降的數據的維護速度。
  • 索引須要佔用物理空間,除了數據表佔數據空間外,每個索引還要佔必定的物理空間,若是要創建聚簇索引,那麼須要的空間就會更大。
  • 建立索引和維護索引要好費時間,這種時間隨着數據量的增長而增長。

4 索引是如何提升查詢速度的?

將無序的數據變成有序的數據(就像查目錄同樣)。 算法

要找到id爲8的記錄簡要步驟:

很明顯的是:沒有用索引咱們是須要遍歷雙向鏈表來定位對應的頁,如今經過 「目錄」 就能夠很快地定位到對應的頁上了!(二分查找,時間複雜度近似爲O(logn))sql

其實底層結構就是B+樹,B+樹做爲樹的一種實現,可以讓咱們很快地查找出對應的記錄。數據庫

5 使用索引注意事項

  • 在常常須要搜索的列上,能夠加快搜索的速度;
  • 在常用where子句的列上建立索引,加快條件的判斷速度;
  • 在常常須要排序的列上建立索引,由於索引已經排序,這樣查詢能夠利用索引的排序,加快排序查詢速度;
  • 對於中到大型表索引都是很是有效的,可是特大型表的話維護開銷會很大,不適合建索引;
  • 在常常用在鏈接的列上建立索引,這些列主要是一些外鍵,能夠加快鏈接的速度;
  • 避免where子句對字段施加函數,只會形成沒法命中索引。
  • 在使用InnoDB時使用與業務無關的自增主鍵做爲主鍵,即便用邏輯主鍵,而不要使用業務主鍵;
  • 將打算加索引的列設置爲NOT NULL,不然將致使引擎放棄使用索引而進行全表掃描;
  • 刪除長期未使用的索引,無用索引的存在會形成沒必要要的性能損耗,MySQL 5.7能夠經過查詢sys庫的chema_unused_indexes視圖來查詢哪些索引從未被使用
  • 在使用limit offset查詢緩慢時,能夠藉助索引來提升性能。

6 MySQL索引主要使用的兩種數據結構

  • 哈希索引

    對於哈希索引來講,底層的數據結構就是哈希表,所以在絕大多數需求爲單條記錄查詢的時候,能夠選擇哈希索引,查詢性能最快;其他大部分場景,建議選擇BTree索引。bash

  • BTree索引

    MySQL的BTree索引使用的是B樹中的B+Tree。但對於主要的兩種存儲引擎(MyIsam和InnoDB)的實現方式是不同的。服務器

7 MySQL索引種類

單列

  • 普通索引:加速查找;
  • 惟一索引:加速查找+約束:不能重複(只能有一個空,否則就重複了);
  • 主鍵:加速查找+約束:不能重複+不能爲空;

多列

  • 聯合索引(多個列建立索引)---->至關於單列的普通索引
  • 聯合惟一索引 ---->至關於單列的惟一索引

    ps:聯合索引的特色:遵循最左前綴的規則數據結構

8 什麼是最左前綴原則?

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子句也遵循此規則。

9 MyIsam和InnoDB實現BTree索引方式的區別

  • MyIsam

    B+Tree葉子節點的data域存放的是數據記錄的地址。在索引檢索的時候,首先按照B+Tree搜索算法搜索索引,若是指定的Key存在,則取出其data域的值,而後data域的值爲地址讀取響應的數據記錄。這被稱爲「非聚簇索引」。

  • InnoDB

    其數據文件自己就是索引文件。MyIsam中,索引文件和數據文件是分離的,而InnoDB中,其表數據文件自己就是按B+Tree組織的一個索引結構,樹的葉節點data域保存了完整的數據記錄。這個索引的key是數據表的主鍵,所以InnoDB表數據文件自己就是主索引。這被稱爲「聚簇索引(或彙集索引)」,而其他的索引都做爲輔助索引,輔助索引的data域存儲相應記錄主鍵的值而不是地址,這也是和MyIsam不一樣的地方。在根據主索引搜索時,直接找到key所在的節點便可取出數據;在根據輔助索引搜索時,則須要先取出主鍵的值,再走一遍主索引。所以,在設計表的時候,不建議使用過長的字段做爲主鍵,也不建議使用非單調的字段做爲主鍵,這會形成主索引頻繁分裂。

【注】更詳細的請出門左轉: MySQL索引和SQL調優

10 覆蓋索引介紹

  • 什麼是覆蓋索引?

    若是一個索引包含(或者說覆蓋)全部須要查詢的字段的值,咱們就稱之爲「覆蓋索引」。咱們知道在InnoDB存儲引擎中,若是不是主鍵索引,葉子節點存儲的是主鍵+列值。最終仍是要「回表」,也就是要經過主鍵再查找一次,這樣就會比較慢。覆蓋索引就是要查詢的列和索引是對應的,不作回表操做。

  • 覆蓋索引使用實例

    加入建立了索引(username,age),在查詢數據的時候:
    select username,age from user where username = ‘Java’ and age = 22;
    要查詢成的列在葉子節點都存在,因此不用回表。

11 選擇索引和編寫利用這些索引查詢的3個原則

  1. 單行訪問和很慢的。特別是在機械硬盤存儲中。若是服務器從存儲中讀取一個數據塊只是爲了獲取其中一行,那麼就浪費了不少工做。最好讀取的塊中能儘量多的包含所須要的行。使用索引建立位置引,用以提升效率。
  2. 按順序訪問範圍數據是很快的,這有兩個緣由:
    • 第一,順序I/O不須要屢次磁盤尋道,因此比隨機I/O要快不少(特別是對機械硬盤)。
    • 第二,若是服務器可以按須要順序讀取數據,那麼就不須要額外的排序操做,而且GROUP BY查詢也無需再作排序和將行按組進行聚合計算了。
  3. 覆蓋索引查詢是很快的。若是一個索引包含了查詢須要的全部列,那麼存儲引擎就不須要再回表查找行,這避免了大量的單行訪問。

12 列舉建立索引可是沒法命中索引的8種狀況

  1. like '%xx'
select * from tb1 where name like '%cn';
複製代碼

以'%'開頭:索引失效。
以'%'結尾:索引能夠用。

  1. 使用函數
select * from tb1 where reverse(name) = 'wupeiqi';
複製代碼
  1. or
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條件中的每一個列中都加上索引.

  1. 類型不一致 若是列類型是字符串,那必定要在條件中將數據使用引號引用起來,不然不使用索引

  2. 含有'!=' 或者'>'

select * from tb1 where name != 'alex'
複製代碼

【特別地】:若是是主鍵或索引整數類型,則仍是會走索引

select * from tb1 where nid > 123
	select * from tb1 where num > 123
複製代碼
  1. 含有'order by'
select email from tb1 order by name desc;
複製代碼

當根據索引排序時,選擇的映射若是不是索引,則不走索引
【特別地】:若是對主鍵排序,則仍是會走索引:

select * from tb1 order by nid desc;
複製代碼
  1. 組合索引最左前綴

若是組合索引爲:(name, email)

- name and email # 使用索引
- name		 # 使用索引
- email		 # 不使用索引
複製代碼
  1. 若是MySQL估計使用全表掃描要比使用索引快,則不使用索引
  2. 其餘:

注:以上內容整理自:

相關文章
相關標籤/搜索