搞懂Mysql索引原理及應用,這一篇就夠了!

什麼是索引?

索引是對數據庫表中一列或多列的值進行排序的一種數據結構,使用索引能夠快速訪問數據庫表中的特定信息。mysql

咱們建立索引的時候是這樣的:算法

create index index_name on table_name(column)

能夠這樣想:索引是取出了一列或者幾個特殊的列,而後把他們重命名爲index_name,而後存起來,存的方式是用特殊的數據結構,好比二叉樹。sql

索引中包含了一個表中列的值和它的地址的值,而且這些值存儲在一個數據結構中。索引的組織形式有二叉樹、哈希、B-樹、B+樹。若是索引存的是一列就叫作單索引,多列就是複合索引。數據庫

索引通常以文件形式存在磁盤中(也能夠存於內存),存儲索引的原理大體歸納爲以空間換時間。數據結構

索引能幹什麼?

執行這樣一條語句:函數

select name from student where stuno = "003"

在一張學生表裏面找學號是003的學生,假如一共到100號學生。那麼DBMS會生成一個相似指針的東西,從stuno ="001"一直到最後,進行全表掃描。那麼爲何到stuno="003"不中止呢?由於數據庫在未添加索引的時候默認執行的是全量搜索,select執行就就是全表掃描,即便掃描主鍵也同樣。性能

若是咱們在stuno字段上建立了一個索引,假如索引的底層存儲結構是二叉樹,那麼只須要log2(100)次就能查到所須要的數據。所以,創建索引的目的就是加快對錶中記錄的查找或排序。mysql索引

固然索引不是萬能的,它的缺點有:建立和維護索引須要時間和空間成本,每次增刪改索引都須要進行動態維護。優化

索引的分類

索引類型有:主鍵索引,普通索引,複合索引,全文索引,惟一索引搜索引擎

  1. 主鍵索引

    --建立1
    crete table student(id int primary key auto_increment, name varchar(20) not null default)
    
    --建立2:先建立表,後建立index
    alter table student add primary key(id)
    
    --在建立主鍵的時候,就會爲咱們建立好主鍵索引。
  2. 惟一索引

    惟一索引和普通索引相似,主要區別在於,惟一索引限制列的值必須惟一,但容許存在空值(只能有一個)。主鍵索引不容許有空值。

  3. 全文索引

    在執行模糊查詢的時候,如like "value%",這種狀況下,須要考慮使用全文搜索的方式進行優化。全文搜索在MySQL中是一個FULLTEXT類型索引。全文索引主要用來查找文本中的關鍵字,而不是直接與索引中的值進行比較,它更像是一個搜索引擎,而不是簡單的where語句的參數匹配。目前只有char/vachar/text列上能夠建立全文索引,默認Mysql不支持中文全文搜索。Mysql全文搜索只是一個臨時方案,對於全文搜索場景,更專業的作法是使用全文搜索引擎,如ElasticSearch。

索引底層原理

哈希表

select * from user where id=7;

哈希算法首先計算出id=7的無力地址addr=hash(7)=4213,而4213映射的物理地址是0x77,而後就能夠找到咱們想要的數據了。

從時間複雜度來看,哈希算法時間複雜度爲o(1),速度很是快,可是Mysql並無採起哈希做爲其底層算法。由於考慮到數據檢索有一個經常使用的手段就是範圍查找,使用哈希就沒有辦法作到高效的範圍查找。好比:

select * from user where id>3

BST樹

二叉查找樹的時間複雜度是O(logn),從檢索效率上看是能作到高效檢索,而二叉樹全部左子樹比根節點小,右子樹比根節點大的特性使得它也支持範圍查找。

可是二叉樹有個致命缺點:極端狀況下會退化爲線性表。在數據庫中,數據的自增是一個很常見的形式,好比主鍵id通常默認都是自增的,若是採起二叉樹做爲索引的底層數據結構,那上面介紹的不平衡狀態致使的線形查找問題必然出現!!!所以,簡單的二叉樹不能直接用於實現Mysql底層索引的。

AVL樹和紅黑樹

學過數據結構的都知道,解決二叉樹不平衡致使的性能問題,就是二叉平衡樹。經過調整樹的結構,來達到高度的平衡狀態,可是頻繁的調整樹的結構也是一種開銷,而紅黑樹就是捨去一部分的平衡性來得到低的調整開銷。可是紅黑樹也存在「右傾現象」,查找性能又會下降。所以在查找性能和調整樹的開銷之間很難找到一種平衡,也不適合做爲Mysql底層索引結構。

img

有人會問:數據庫查詢數據的瓶頸在於磁盤IO,調整樹的開銷存在於增刪,而查找不須要調整,爲何AVL樹不適合呢?

答:AVL不是不適合,而是有更好的數據結構。咱們知道磁盤IO有一個特色,就是從磁盤讀取1B的數據和1KB的數據所消耗的時間基本是同樣的,那咱們的優化思路就能夠改成:儘量在一次磁盤 IO 中多讀一點數據到內存。這個直接反映到樹的結構就是,每一個節點能存儲的 key 能夠適當增長,這就是B樹、B+樹的設計原理了。

img

B樹和B+樹

B樹和B+樹又被稱爲多路查找,一個節點存儲了多個key來減小磁盤IO,從而提升檢索速度。

img

B樹和B+樹有什麼區別呢?

  • B樹一個節點裏面存的是(key&value),而B+樹存儲的是(key),因此B樹裏一個節點存不了不少(key&value),可是B+樹一個節點能存儲不少(key),B+樹葉子節點存全部的數據。

    img

  • B+樹的葉子節點是(key&value,並用一個鏈表串聯起來。

    img

經過 B 樹和 B+樹的對比咱們看出,B+樹節點存儲的是索引,在單個節點存儲容量有限的狀況下,單節點也能存儲大量索引,使得整個 B+樹高度下降,減小了磁盤 IO。其次,B+樹的葉子節點是真正數據存儲的地方,葉子節點用了鏈表鏈接起來,這個鏈表自己就是有序的,在數據範圍查找時,更具有效率。所以 Mysql 的索引用的就是 B+樹,B+樹在查找效率、範圍查找中都有着很是不錯的性能。

Mysql存儲引擎實現

Mysql底層數據引擎以插件形式設計,最多見的就是Innodb引擎和Myisam引擎,用戶能夠根據我的需求選擇不一樣的引擎做爲Mysql數據表的底層引擎。咱們剛分析了,B+樹做爲 Mysql 的索引的數據結構很是合適,可是數據和索引到底怎麼組織起來也是須要一番設計,設計理念的不一樣也致使了 Innodb 和 Myisam 的出現,各自呈現獨特的性能。

MyISAM 雖然數據查找性能極佳,可是不支持事務處理。Innodb 最大的特點就是支持了 ACID 兼容的事務功能,並且他支持行級鎖。Mysql 創建表的時候就能夠指定引擎,好比下面的例子,就是分別指定了 Myisam 和 Innodb 做爲 user 表和 user2 表的數據引擎。

create table 'user' (
'id' int not null default '0'
) engine=myisam default charset=utf8

Innodb建立表後生成的文件有:

  • frm:建立表的語句
  • idb:表裏面的數據+索引文件

Myisam建立表後生成的文件有:

  • frm:建立表的語句
  • MYD:表裏面的數據文件
  • MYI:表裏面的索引文件

從生成的文件看來,這兩個引擎底層數據和索引的組織方式並不同,MyISAM 引擎把數據和索引分開了,一人一個文件,這叫作非彙集索引方式;Innodb 引擎把數據和索引放在同一個文件裏了,這叫作彙集索引方式。下面將從底層實現角度分析這兩個引擎是怎麼依靠 B+樹這個數據結構來組織引擎實現的。

MyISAM引擎的底層實現(非彙集索引方式)

MyISAM 用的是非彙集索引方式,即數據和索引落在不一樣的兩個文件上。MyISAM 在建表時以主鍵做爲 KEY 來創建主索引 B+樹,樹的葉子節點存的是對應數據的物理地址。咱們拿到這個物理地址後,就能夠到 MyISAM 數據文件中直接定位到具體的數據記錄了。

img

當咱們爲某個字段添加索引時,咱們一樣會生成對應字段的索引樹,該字段的索引樹的葉子節點一樣是記錄了對應數據的物理地址,而後也是拿着這個物理地址去數據文件裏定位到具體的數據記錄。

Innodb引擎的底層實現(彙集索引方式)

InnoDB 是彙集索引方式,所以數據和索引都存儲在同一個文件裏。首先 InnoDB 會根據主鍵 ID 做爲 KEY 創建索引 B+樹,如左下圖所示,而 B+樹的葉子節點存儲的是主鍵 ID 對應的數據,好比在執行 select * from user_info where id=15 這個語句時,InnoDB 就會查詢這顆主鍵 ID 索引 B+樹,找到對應的 user_name='Bob'。

img

這是建表的時候 InnoDB 就會自動創建好主鍵 ID 索引樹,這也是爲何 Mysql 在建表時要求必須指定主鍵的緣由。當咱們爲表裏某個字段加索引時 InnoDB 會怎麼創建索引樹呢?好比咱們要給 user_name 這個字段加索引,那麼 InnoDB 就會創建 user_name 索引 B+樹,節點裏存的是 user_name 這個 KEY,葉子節點存儲的數據的是主鍵 KEY。注意,葉子存儲的是主鍵 KEY!拿到主鍵 KEY 後,InnoDB 纔會去主鍵索引樹里根據剛在 user_name 索引樹找到的主鍵 KEY 查找到對應的數據。

問題來了,爲何 InnoDB 只在主鍵索引樹的葉子節點存儲了具體數據,可是其餘索引樹卻不存具體數據呢,而要畫蛇添足先找到主鍵,再在主鍵索引樹找到對應的數據呢?

其實很簡單,由於 InnoDB 須要節省存儲空間。一個表裏可能有不少個索引,InnoDB 都會給每一個加了索引的字段生成索引樹,若是每一個字段的索引樹都存儲了具體數據,那麼這個表的索引數據文件就變得很是巨大(數據極度冗餘了)。從節約磁盤空間的角度來講,真的沒有必要每一個字段索引樹都存具體數據,經過這種看似「畫蛇添足」的步驟,在犧牲較少查詢的性能下節省了巨大的磁盤空間,這是很是有值得的。

兩種存儲引擎對比

在進行 InnoDB 和 MyISAM 特色對比時談到,MyISAM 查詢性能更好,從上面索引文件數據文件的設計來看也能夠看出緣由:MyISAM 直接找到物理地址後就能夠直接定位到數據記錄,可是 InnoDB 查詢到葉子節點後,還須要再查詢一次主鍵索引樹,才能夠定位到具體數據。等於 MyISAM 一步就查到了數據,可是 InnoDB 要兩步,那固然 MyISAM 查詢性能更高。

使用索引的注意事項!!!!

Mysql索引使用策略

  1. 最好全值匹配--索引怎麼建我怎麼用。
  2. 最佳左前綴法則--若是是多列複合索引,要遵照最左前綴法則。指的是查詢要從索引的最左前列開始而且不跳過索引中的列。
  3. 不在索引列上作任何操做(計算,函數,(自動或者手動)類型裝換),會致使索引失效而致使全表掃描。
  4. 存儲引擎不能使用索引中範圍條件右邊的列。--範圍以後索引失效(< ,>,between and)
  5. 儘可能使用覆蓋索引--索引和查詢列一致,減小select *。--按需取數據用多少取多少
  6. 在MYSQL使用不等於(<,>,!=)的時候沒法使用索引,會致使索引失效
  7. is null或者is not null 也會致使沒法使用索引
  8. like以通配符開頭('%abc...')MYSQL索引失效會變成全表掃描的操做。--覆蓋索引
  9. 隱式轉換索引失效:字符串不加單引號
  10. where條件少用or,用它來鏈接時索引會失效

Mysql索引使用原則

  1. 複合索引:選擇索引列的順序

    1)儘可能把字段長度小的列放在聯合索引的最左側(由於字段長度越小,一頁能存儲的數據量越大,IO性能也就越好)
    
    2)區分度最高的放在聯合索引的最左側(區分度=列中不一樣值的數量/列的總行數)
    
    3)使用最頻繁的列放到聯合索引的左側(這樣能夠比較少的創建一些索引)
  2. 表關聯查詢

    1)類型和大小要相同,可使用索引。
    
    VARCHAR(10)和 CHAR(10)大小相同,但 VARCHAR(10)與 CHAR(15)不相同。
    
    2)字符串列之間比較,兩列應使用相同的字符集。例如,將utf8列與 latin1列進行比較會不使用索引。
    
    3)將字符串列與時間或數字列進行比較時,在沒有轉換狀況下,不使用索引。

什麼字段適合建立索引?

  1. 較爲頻繁的做爲查詢條件的字段應該建立索引
  2. 惟一性太差的字段不適合單首創建索引,即便該字段頻繁做爲查詢條件
  3. 更新很是頻繁的字段不適合建立索引
相關文章
相關標籤/搜索