8、索引

1、索引不是萬能

  • 數據行數少的狀況下,索引效率沒什麼做用
  • 當數據重複度大,好比高於 10% 的時候,也不須要對這個字段使用索引

2、索引類型

1.功能邏輯分類

普通索引
  • 基礎的索引,沒有任何約束,主要用於提升查詢效率
惟一索引
  • 在普通索引的基礎上增長了數據惟一性的約束
  • 在一張數據表裏能夠有多個惟一索引
主鍵索引
  • 在惟一索引的基礎上增長了不爲空的約束,也就是 NOT NULL+UNIQUE
  • 一張表裏最多隻有一個主鍵索引
全文索引
  • 用的很少
  • MySQL 自帶的全文索引只支持英文
  • 用專門的全文搜索引擎更好(ElasticSearch和Solr)

 2.物理實現分類

彙集索引
  • 有且只能有一個
  • 表中數據行按索引的排序方式進行存儲,影響數據表的物理存儲順序,對查找行頗有效
  • 葉子節點存儲的就是咱們的數據記錄
  • 只有當表包含彙集索引時,表內的數據行纔會按找索引列的值在磁盤上進行物理排序和存儲
  • 每個表只能有一個彙集索引,由於數據行自己只能按一個順序存儲
  • 相比非彙集索引查詢更高效
非彙集索引
  • 能夠沒有,也有多個
  • 又稱二級索引或者輔助索引
  • 單獨的存儲空間存放非彙集索引
  • 索引項是按照順序存儲的,索引項指向的內容是隨機存儲的
  • 每一個非彙集索引保存的數據都會存儲主鍵值
  • 兩次查找,第一次先找到索引,第二次找到索引對應的位置取出數據行
  • 非彙集索引不會把索引指向的內容像彙集索引同樣直接放到索引的後面,而是維護單獨的索引表
  • 只維護索引,不維護索引指向的數據
  • 相比彙集索引,插入,刪除,更新等操做效率更高

 3.字段個數分類

單一索引 索引列爲一列
聯合索引 多個列組合在一塊兒建立

4.索引片中包含的匹配列的數量不一樣

窄索引 包含索引列數爲 1 或 2
寬索引 包含的索引列數大於 2)

3、聯合索引的最左原則

聯合索引(x,y,z),索引查詢,必須出現由左到右的查詢條件纔有效(不是編寫SQL的where順序,是要查y的時候,必有x做爲查詢條件才能夠),從左到右的使用索引中的字段sql

4、爲何用B+樹來作索引

名稱 樹形 侷限性
二叉樹

  1. 可能深度很是大
  2. 有時候還不靠譜,成了鏈表
平衡二叉樹
  1. 增長約束每一個節點左子樹和右子樹的高度不能超過1
  2. 當 n 比較大時,深度也是比較高的
  3. 執行增刪操做,不知足上述條件就要經過旋轉來保持平衡
  4. 旋轉是很是耗時的,因此AVL樹適合用於查找多的狀況
BTree
  1. 分叉從2變成M階
  2. 下降了深度查詢的次數
  3. 非葉子節點也會存儲數據,查詢效率不穩定
B+Tree
  1. 中間節點並不直接存儲數據
  2. 每次只有訪問到葉子節點才能找到對應的數據
  3. 查詢效率更穩定
  4. 樹更矮胖(階數更大深度更低)、查詢效率更高
  5. 葉子節點經過有序鏈表進行了連接範圍查詢上大大提升
  • 磁盤的 I/O 操做次數對索引的使用效率相當重要
  • 雖然傳統的二叉樹數據結構查找數據的效率高,但很容易增長磁盤 I/O 操做的次數,影響索引使用的效率
  • 在構造索引的時候,更傾向於採用「矮胖」的數據結構
  • B 樹和 B+ 樹均可以做爲索引的數據結構,在 MySQL 中採用的是 B+ 樹,B+ 樹在查詢性能上更穩定,在磁盤頁大小相同的狀況下,樹的構造更加矮胖,所須要進行的磁盤 I/O 次數更少
  • 子節點進行了鏈表連接更適合進行關鍵字的範圍查詢 

5、Hash索引

  • Hash 自己是一個函數,又被稱爲散列函數,它能夠幫助咱們大幅提高檢索數據的效率
  • 只須要一次交互就能夠完成查找,效率很是高
  Hash B+Tree
範圍查詢
  • 不支持,索引無序
  • 支持,葉子節點是有序鏈表
聯合索引的最左側原則
  • 聯合索引的部分索引沒法使用
  • Hash 索引在計算 Hash 值的時候是將索引鍵合併後再一塊兒計算 Hash 值,因此不會針對每一個索引單獨計算 Hash 值
  • 支持,聯合左向右關聯
ORDER BY 排序
  • Hash 索引指向的數據是無序的,所以沒法起到排序優化的做用
  • B+ 樹索引數據是有序的,能夠起到對該字段 ORDER BY 排序優化的做用
模糊查詢
  • 不支持,同上
  • 支持,xxx%
等值查詢
  • 效率更高
  • 索引列的重複值若是不少,效率就會下降
  • Hash 索引一般不會用到重複值多的列上
  • 效率比不上Hash

MySQL 中的 Memory 存儲引擎支持 Hash 存儲,其餘不支持,如InnoDB數據庫

6、Mysql 自適應 Hash 索引

MySQL 的 InnoDB 存儲引擎還有個「自適應 Hash 索引」的功能,就是當某個索引值使用很是頻繁的時候,它會在 B+ 樹索引的基礎上再建立一個 Hash 索引,這樣讓 B+ 樹也具有了 Hash 索引的優勢,這個功能不須要人工干預數組

  1. 自適應哈希索引只保存熱數據(常常被使用到的數據),並不是全表數據。所以數據量並不會很大,可讓自適應Hash放到緩衝池中,也就是InnoDB buffer pool,進一步提高查找效率
  2. InnoDB中的自適應Hash至關因而「索引的索引」,採用Hash索引存儲的是B+樹索引中的頁面的地址
  3. 自適應Hash採用Hash函數映射到一個哈希表中,因此對於字典類型的數據查找很是方便哈希表是數組+鏈表的形式

InnoDB自己不支持Hash,可是提供自適應Hash索引,不須要用戶來操做,而是存儲引擎自動完成的。自適應Hash也是InnoDB三大關鍵特性之一數據結構

7、建立索引規律

索引太多了,在更新數據的時候,若是涉及到索引更新,就會形成負擔函數

須要建立索引的狀況性能

  1. 字段的數值有惟一性的限制
  2. 頻繁做爲 WHERE 查詢條件的字段,尤爲在數據表大的狀況下
  3. 須要常常 GROUP BY 和 ORDER BY 的列
  4. 多個單列索引在多條件查詢時只會生效一個索引(MySQL 會選擇其中一個限制最嚴格的做爲索引),多條件聯合查詢的時候最好建立聯合索引
  5. UPDATE、DELETE 的 WHERE 條件列,通常也須要建立索引
  6. DISTINCT 字段須要建立索引

不須要建立索引的狀況優化

  1. WHERE 條件(包括 GROUP BY、ORDER BY)裏用不到的字段不須要建立索引
  2. 若是表記錄太少,好比少於 1000 個,那麼是不須要建立索引的
  3. 字段中若是有大量重複數據,也不用建立索引
  4. 頻繁更新的字段不必定要建立索引,更新數據的時候,也須要更新索引,若是索引太多,在更新索引的時候也會形成負擔,從而影響效率

索引失效的狀況搜索引擎

  1. 若是索引進行了表達式計算,則會失效
  2. 若是對索引使用函數,會形成失效
  3. 在 WHERE 子句中,若是在 OR 前的條件列進行了索引,而在 OR 後的條件列沒有進行索引,那麼索引會失效,由於 OR 的含義就是兩個只要知足一個便可,所以只有一個條件列進行了索引是沒有意義的,只要有條件列沒有進行索引,就會進行全表掃描,所以索引的條件列也會失效
  4. 當咱們使用 LIKE 進行模糊查詢的時候,前面不能是 %,如%xxx
  5. 索引列儘可能設置爲 NOT NULL 約束
  6. 聯合索引的時候要注意最左原則

8、索引片和過濾因子

  1. SQL 查詢語句在執行中須要掃描的一個索引片斷
  2. 索引片越寬,須要順序掃描的索引頁就越多
  3. 索引片越窄,就會減小索引訪問的開銷

經過寬索引避免回表

學生表(學號,姓名,課程ID,課程名),主鍵(學號)spa

select 學號,姓名,課程ID,課程名 from 學生表 where 課程ID in (1,2,3);

窄索引(課程ID)設計

  1. 經過窄索引,找到主鍵(定位數據行)
  2. 根據主鍵(數據行),回表查找相應數據
  3. 取出select須要的數據

寬索引(課程ID,姓名,課程名)

  • 直接經過寬索引找到select須要的數據

優勢:經過寬索引將 SELECT 中須要用到的列(主鍵列能夠除外)都設置在寬索引中,這樣就避免了回表掃描的狀況

缺點:寬索引須要順序掃描的索引頁不少

過濾因子

  • 描述了謂詞的選擇性。在 WHERE 條件語句中,每一個條件都稱爲一個謂詞,謂詞的選擇性也等於知足這個條件列的記錄數除以總記錄數的比例

學生表(學號,姓名,課程ID,性別,年齡

  • 假設重複率:性別50%,姓名14%,課程ID28%,年齡43%
  • 性別,年齡不是好過濾因子
  • 聯合過濾因子有更高的過濾能力
  • 須要注意一個條件,那就是條件列的關聯性應該儘可能相互獨立,不然若是列與列之間具備相關性,聯合過濾因子的能力就會降低不少
  • 好比城市名稱和電話區號就有強相關性,這兩個列組合到一塊兒不會增強過濾效果
  • 過濾因子決定了索引片的大小
  • 過濾因子的條件過濾能力越強,知足條件的記錄數就越少,SQL 查詢須要掃描的索引片也就越小
  • 若是沒有選擇好索引片中的過濾因子,就會形成索引片中的記錄數過多的狀況

三星索引

  1. 在 WHERE 條件語句中,找到全部等值謂詞中的條件列,將它們做爲索引片中的開始列
  2. 將 GROUP BY 和 ORDER BY 中的列加入到索引中
  3. 將 SELECT 字段中剩餘的列加入到索引片中
  • 經過索引查找符合條件的記錄,就須要將 WHERE 子句中的等值謂詞列加入到索引片中,這樣索引的過濾能力越強,最終掃描的數據行就越少
  • 要對數據記錄分組或者排序,都須要從新掃描數據記錄。爲了不進行 file sort 排序,能夠把 GROUP BY 和 ORDER BY 中涉及到的列加入到索引中
  • 建立了索引就會按照索引的順序來存儲數據,這樣再對這些數據按照某個字段進行分組或者排序的時候,就會提高效率

很難存在理想的索引設計

  1. 採用三星索引會讓索引片變寬,這樣每一個頁可以存儲的索引數據就會變少,從而增長了頁加載的數量
  2. 從另外一個角度來看,若是數據量很大,好比有 1000 萬行數據,過多索引所須要的磁盤空間可能會成爲一個問題,對緩衝池所需空間的壓力也會增長,增長了索引維護的成本
  3. 若是爲全部的查詢語句都設計理想的三星索引,就會讓數據表中的索引個數過多,這樣索引維護的成本也會增長
  4. 當添加一條記錄的時候,就須要在每個索引上都添加相應的行(存儲對應的主鍵值)
  5. 假設添加一行記錄的時間成本是 10ms(磁盤隨機讀取一個頁的時間)
  6. 若是咱們建立了 10 個索引,添加一條記錄的時間就可能變成 0.1s,若是是添加 10 條記錄呢?就會花費近 1s 的時間
  7. 從索引維護的成原本看消耗仍是很高的
  8. 固然對於數據庫來講,數據的更新不必定立刻回寫到磁盤上,但即便不及時將髒頁進行回寫,也會形成緩衝池中的空間佔用過多,髒頁過多的狀況
相關文章
相關標籤/搜索