mysql索引總結


什么是索引?

索引是一種用於快速查詢和檢索數據的數據結構。常見的索引結構有: B樹, B+樹和Hash。

索引的作用就相當於目錄的作用。打個比方: 我們在查字典的時候,如果沒有目錄,那我們就只能一頁一頁的去找我們需要查的那個字,速度很慢。如果有目錄了,我們只需要先去目錄里查找字的位置,然后直接翻到那一頁就行了。

為什么要用索引?索引的優缺點分析

優點:

  1. 大大加快數據的檢索速度(大大減少檢索的數據量)-------創建索引的最主要因素
  2. 通過建立唯一索引,保證數據的唯一性
  3. 幫助服務器避免排序和臨時表
  4. 隨機I/O變成順序I/O
  5. 可以加速表和表質檢的連接,特別是實現數據完整性方面特別有意義

缺點:

  1.創建索引和維護索引需要很多時間。這種時間隨着數據量的增加而增加。

  2.如果一個數據建立了索引,那么增刪改這個數據,相應的索引也要進行動態修改,這將大大降低sql的執行效率。

  3.需要占用物理存儲空間:索引需要使用物理文件存儲,也會耗費一定空間。

Mysql索引主要使用的三種數據結構

哈希索引、BTree(B樹索引、B+樹索引)

Hash索引和 B+樹索引優劣分析

Hash索引定位快

Hash索引指的就是Hash表,最大的優點就是能夠在很短的時間內,根據Hash函數定位到數據所在的位置,這是B+樹所不能比的。

Hash沖突問題

知道HashMap或HashTable的同學,相信都知道它們最大的缺點就是Hash沖突了。不過對於數據庫來說這還不算最大的缺點。

Hash索引不支持順序和范圍查詢(Hash索引不支持順序和范圍查詢是它最大的缺點。

試想一種情況:

SELECT * FROM tb1 WHERE id < 500;

缺點: 

  • 哈希索引也沒辦法利用索引完成排序
  • 不支持最左匹配原則
  • 在有大量重復鍵值情況下,哈希索引的效率也是極低的---->哈希碰撞問題。
  • 不支持范圍查詢

B+樹是有序的,在這種范圍查詢中,優勢非常大,直接遍歷比500小的葉子節點就夠了。而Hash索引是根據hash算法來定位的,難不成還要把 1 - 499的數據,每個都進行一次hash計算來定位嗎?這就是Hash最大的缺點了。

B樹和B+樹區別

  • B樹的所有節點既存放 鍵(key) 也存放 數據(data);而B+樹只有葉子節點存放 key 和 data,其他內節點只存放key。
  • B樹的葉子節點都是獨立的;B+樹的葉子節點有一條引用鏈指向與它相鄰的葉子節點。
  • B樹的檢索的過程相當於對范圍內的每個節點的關鍵字做二分查找,可能還沒有到達葉子節點,檢索就結束了。而B+樹的檢索效率就很穩定了,任何查找都是從根節點到葉子節點的過程,葉子節點的順序檢索很明顯。

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

MyISAM

B+Tree葉節點的data域存放的是數據記錄的地址。在索引檢索的時候,首先按照B+Tree搜索算法搜索索引,如果指定的Key存在,則取出其 data 域的值,然后以 data 域的值為地址讀取相應的數據記錄。這被稱為“非聚簇索引”。

InnoDB

其數據文件本身就是索引文件。相比MyISAM,索引文件和數據文件是分離的,其表數據文件本身就是按B+Tree組織的一個索引結構,樹的葉節點data域保存了完整的數據記錄。這個索引的key是數據表的主鍵,因此InnoDB表數據文件本身就是主索引。這被稱為“聚簇索引(或聚集索引)”,而其余的索引都作為輔助索引,輔助索引的data域存儲相應記錄主鍵的值而不是地址,這也是和MyISAM不同的地方。在根據主索引搜索時,直接找到key所在的節點即可取出數據;在根據輔助索引查找時,則需要先取出主鍵的值,在走一遍主索引。 因此,在設計表的時候,不建議使用過長的字段作為主鍵,也不建議使用非單調的字段作為主鍵,這樣會造成主索引頻繁分裂。 PS:整理自《Java工程師修煉之道》

聚集索引與非聚集索引

聚集索引

聚集索引即索引結構和數據一起存放的索引。主鍵索引屬於聚集索引

mysql的InnoDB引擎的表的.ibd文件就包含了該表的索引和數據。對於 InnoDB 引擎表來說,非葉子節點包含索引,葉子節點包含索引和對應數據

聚集索引的優點

聚集索引查找的速度非常的快,因為整個B+樹本身就是的多叉平衡樹,葉子節點是有序的,所以找到了索引就等於是找到了相應的數據

聚集索引的缺點

1.依賴於有序的數據。B+樹本身就是多叉平衡樹,如果索引的數據不是有序的,那么就需要在插入的時候進行排序,如果數據是整刑還比較好操作,如果是字符串或者是uuid這種又長又難比較的數據,插入和查找的速度肯定就很慢了

2.更新代價比較大。如果索引列的數據被修改,那么相應的索引也要修改,加上聚集索引的葉子節點上還存放了數據,修改的代價就更大了。所以對於主鍵索引來說,一般主鍵是不允許修改的。

非聚集索引

非聚集索引即索引結構和數據分開存放的索引。

二級索引屬於非聚集索引。

MYISAM引擎的表的.MYI文件包含了表的索引, 該表的索引(B+樹)的每個葉子非葉子節點存儲索引, 葉子節點存儲索引和索引對應數據的指針,指向.MYD文件的數據。

非聚集索引的葉子節點並不一定存放數據的指針, 因為二級索引的葉子節點就存放的是主鍵,根據主鍵再回表查數據。

非聚集索引的優點

更新代價比聚集索引要小 。非聚集索引的更新代價就沒有聚集索引那么大了,非聚集索引的葉子節點是不存放數據的

非聚集索引的缺點

  1. 跟聚集索引一樣,非聚集索引也依賴於有序的數據
  2. 可能會二次查詢(回表) :這應該是非聚集索引最大的缺點了。 當查到索引對應的指針或主鍵后,可能還需要根據指針或主鍵再到數據文件或表中查詢。

這是Mysql的表的文件截圖:

 聚集索引和非聚集索引:

非聚集索引一定回表查詢嗎(覆蓋索引)?

非聚集索引不一定回表查詢。

試想一種情況,用戶准備使用SQL查詢用戶名,而用戶名字段正好建立了索引。

 SELECT name FROM table WHERE username='guang19';

那么這個索引的key本身就是name,查到對應的name直接返回就行了,無需回表查詢。

即使是MYISAM也是這樣,雖然MYISAM的主鍵索引確實需要回表, 因為它的主鍵索引的葉子節點存放的是指針。但是如果SQL查的就是主鍵呢?

SELECT id FROM table WHERE id=1;

主鍵索引本身的key就是主鍵,查到返回就行了。這種情況就稱之為覆蓋索引了。

覆蓋索引介紹

什么是覆蓋索引

如果一個索引中包含相應的數據,我們就稱之為覆蓋索引。在InnoDB存儲引擎中 如果不是主鍵索引,葉子節點存儲的就是主鍵+列值,最終還是要“回表”,也就是要通過主鍵再查找一次,這樣就會比較慢。覆蓋索引就是把查詢出的列和索引是對應的,不做回表操作。

覆蓋索引即需要查詢的字段正好是索引的字段,那么直接根據該索引,就可以查到數據了, 而無需回表查詢。

如主鍵索引,如果一條SQL需要查詢主鍵,那么正好根據主鍵索引就可以查到主鍵。

再如普通索引,如果一條SQL需要查詢name,name字段正好有索引, 那么直接根據這個索引就可以查到數據,也無需回表。

覆蓋索引:

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

  1. 單行訪問是很慢的。特別是在機械硬盤存儲中(SSD的隨機I/O要快很多,不過這一點仍然成立)。如果服務器從存儲中讀取一個數據塊只是為了獲取其中一行,那么就浪費了很多工作。最好讀取的塊中能包含盡可能多所需要的行。使用索引可以創建位置引,用以提升效率。
  2. 按順序訪問范圍數據是很快的,這有兩個原因。第一,順序1/0不需要多次磁盤尋道,所以比隨機I/O要快很多(特別是對機械硬盤)。第二,如果服務器能夠按需要順序讀取數據,那么就不再需要額外的排序操作,並且GROUPBY查詢也無須再做排序和將行按組進行聚合計算了。
  3. 索引覆蓋查詢是很快的。如果一個索引包含了查詢需要的所有列,那么存儲引擎就 不需要再回表查找行。這避免了大量的單行訪問,而上面的第1點已經寫明單行訪 問是很慢的。

索引創建原則

單列索引

單列索引即由一列屬性組成的索引。

聯合索引(多列索引)

聯合索引即由多列屬性組成索引。

最左前綴原則

假設創建的聯合索引由三個字段組成:

ALTER TABLE table ADD INDEX index_name (num,name,age)

那么當查詢的條件有為:num / (num AND name) / (num AND name AND age)時,索引才生效。所以在創建聯合索引時,盡量把查詢最頻繁的那個字段作為最左(第一個)字段。查詢的時候也盡量以這個字段為第一條件。

但可能由於版本原因(我的mysql版本為8.0.x),我創建的聯合索引,相當於在聯合索引的每個字段上都創建了相同的索引:

無論是否符合最左前綴原則,每個字段的索引都生效:

 

索引創建注意點

最左前綴原則

雖然我目前的Mysql版本較高,好像不遵守最左前綴原則,索引也會生效。 但是我們仍應遵守最左前綴原則,以免版本更迭帶來的麻煩。

選擇合適的字段

1.不為NULL的字段

索引字段的數據應該盡量不為NULL,因為對於數據為NULL的字段,數據庫較難優化。如果字段頻繁被查詢,但又避免不了為NULL,建議使用0,1,true,false這樣語義較為清晰的短值或短字符作為替代。

2.被頻繁查詢的字段

我們創建索引的字段應該是查詢操作非常頻繁的字段。

3.被作為條件查詢的字段

被作為WHERE條件查詢的字段,應該被考慮建立索引。

4.被經常頻繁用於連接的字段

經常用於連接的字段可能是一些外鍵列,對於外鍵列並不一定要建立外鍵,只是說該列涉及到表與表的關系。對於頻繁被連接查詢的字段,可以考慮建立索引,提高多表連接查詢的效率。

不合適創建索引的字段

1.被頻繁更新的字段應該慎重建立索引

雖然索引能帶來查詢上的效率,但是維護索引的成本也是不小的。 如果一個字段不被經常查詢,反而被經常修改,那么就更不應該在這種字段上建立索引了。

2.不被經常查詢的字段沒有必要建立索引

3.盡可能的考慮建立聯合索引而不是單列索引

因為索引是需要占用磁盤空間的,可以簡單理解為每個索引都對應着一顆B+樹。如果一個表的字段過多,索引過多,那么當這個表的數據達到一個體量后,索引占用的空間也是很多的,且修改索引時,耗費的時間也是較多的。如果是聯合索引,多個字段在一個索引上,那么將會節約很大磁盤空間,且修改數據的操作效率也會提升。

4.注意避免冗余索引

冗余索引指的是索引的功能相同,能夠命中 就肯定能命中 ,那么 就是冗余索引如(name,city )和(name )這兩個索引就是冗余索引,能夠命中后者的查詢肯定是能夠命中前者的 在大多數情況下,都應該盡量擴展已有的索引而不是創建新索引。

5.考慮在字符串類型的字段上使用前綴索引代替普通索引

前綴索引僅限於字符串類型,較普通索引會占用更小的空間,所以可以考慮使用前綴索引帶替普通索引。

使用索引一定能提高查詢性能嗎?

大多數情況下,索引查詢都是比全表掃描要快的。但是如果數據庫的數據量不大,那么使用索引也不一定能夠帶來很大提升。

為什么索引能提高查詢速度:

先從 MySQL 的基本存儲結構說起

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

 

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

所以說,如果我們寫select * from user where indexname = 'xxx'這樣沒有進行任何優化的sql語句,默認會這樣做:

  1. 定位到記錄所在的頁:需要遍歷雙向鏈表,找到所在的頁
  2. 從所在的頁內中查找相應的記錄:由於不是根據主鍵查詢,只能遍歷所在頁的單鏈表了

很明顯,在數據量很大的情況下這樣查找會很慢!這樣的時間復雜度為O(n)。

使用索引之后

索引做了些什么可以讓我們查詢加快速度呢?其實就是將無序的數據變成有序(相對):

 

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

很明顯的是:沒有用索引我們是需要遍歷雙向鏈表來定位對應的頁,現在通過 “目錄” 就可以很快地定位到對應的頁上了!(二分查找,時間復雜度近似為O(logn))

其實底層結構就是B+樹,B+樹作為樹的一種實現,能夠讓我們很快地查找出對應的記錄。

關於索引其他重要的內容補充

最左前綴原則

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,那么現在的查詢引擎會自動優化為匹配聯合索引的順序,這樣是能夠命中索引的。

由於最左前綴原則,在創建聯合索引時,索引字段的順序需要考慮字段值去重之后的個數,較多的放前面。ORDER BY子句也遵循此規則。

注意避免冗余索引

冗余索引指的是索引的功能相同,能夠命中 就肯定能命中 ,那么 就是冗余索引如(name,city )和(name )這兩個索引就是冗余索引,能夠命中后者的查詢肯定是能夠命中前者的 在大多數情況下,都應該盡量擴展已有的索引而不是創建新索引。

MySQL 5.7 版本后,可以通過查詢 sys 庫的 schema_redundant_indexes 表來查看冗余索引

Mysql如何為表字段添加索引???

1.添加PRIMARY KEY(主鍵索引)

ALTER TABLE `table_name` ADD PRIMARY KEY ( `column` ) 

2.添加UNIQUE(唯一索引)

ALTER TABLE `table_name` ADD UNIQUE ( `column` ) 

3.添加INDEX(普通索引)

ALTER TABLE `table_name` ADD INDEX index_name ( `column` )

4.添加FULLTEXT(全文索引)

ALTER TABLE `table_name` ADD FULLTEXT ( `column`) 

5.添加多列索引

ALTER TABLE `table_name` ADD INDEX index_name ( `column1`, `column2`, `column3` )

參考:

https://github.com/Snailclimb/JavaGuide/blob/master/docs/database/MySQL%20Index.md

https://juejin.im/post/5b55b842f265da0f9e589e79

https://github.com/Snailclimb/JavaGuide/blob/master/docs/database/%E6%95%B0%E6%8D%AE%E5%BA%93%E7%B4%A2%E5%BC%95.md#%E6%9C%80%E5%B7%A6%E5%89%8D%E7%BC%80%E5%8E%9F%E5%88%99-1

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM