MyISAM存儲引擎的索引和InnoDB存儲引擎的索引


MyISAM存儲引擎的索引和InnoDB存儲引擎的索引

​ MyISAM和InnoDB這兩個存儲引擎都使用B+樹作為索引的結構,但是這兩種存儲引擎對索引的具體實現方式方面是不同的。下面來具體介紹一下這兩種存儲引擎的索引具體是如何實現的。

MyISAM存儲引擎

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

​ 如圖所示,以Col1列作為主鍵建立的主索引:

​ 非聚簇索引是一種索引結構和數據分開存放的索引,該索引中索引的邏輯順序與數據庫表行中數據的物理順序不同。

在MyISAM存儲引擎中,只要索引值不重復的索引都被稱為主索引。 所以MyISAM存儲引擎中可以從在多個主索引。

在MyISAM存儲引擎中,索引值重復的索引都被稱為輔助索引(又稱二級索引)。如下圖所示,為Col2列建立的輔助索引的結構示意圖:

​ 在MyISAM存儲引擎中,無論是主索引還是輔索引,他們的葉子節點都保存的是數據的地址,因此多個索引之間可以保持同步的關系。

​ 當我們在MyISAM存儲引擎中創建一個表時,這個表會相應生成三個文件

​ 1、.frm文件,這是表定義文件。

​ 2、.myi文件,這個表存儲了數據的索引。

​ 3、.md文件,這個表存儲的是數據。

InnoDB存儲引擎

​ InnoDB引擎中,其數據文件本身就是索引文件。相比MyISAM,索引文件和數據文件是分離的,其表數據文件本身就是按B+Tree組織的一個索引結構,樹的葉子節點data域保存了完整的數據記錄(在Mysql中,InnoDB 引擎的表的 .ibd文件就包含了該表的索引和數據)。這個索引的key是數據表的主鍵,因此InnoDB表數據文件本身就是主索引,即數據表的主鍵列使用的就是主索引。

​ 如圖所示為一個數據庫表的主鍵索引的結構示意圖:

主鍵索引和輔助索引

​ 數據表的主鍵列使用的索引就是主鍵索引。一張數據表有只能有一個主鍵,並且主鍵不能為 null,不能重復。許多情況下我們在建好表之后,並沒有主動的建立索引,此時索引是系統幫助我們創建的。其創建的過程如下:

​ 在我們創建好表之后,系統會查看表中有沒有主鍵,如果我們設置了主鍵,那么系統就會根據這個主鍵來建立一個主鍵索引,接着將這個主鍵索引當為主索引(主索引不允許索引值重復,也不允許值為null)。

​ 如果表中沒有設置主鍵,那么系統就會查看表中有沒有唯一索引的字段,如果有,那么系統就會將這個唯一索引的字段當為主索引(主索引不允許索引值重復)。

​ 如果表中沒有唯一索引的字段,此時系統就會在表中添加一個隱含字段,這個字段的大小為6字節。接着系統就會根據這個隱藏字段來建立索引,此時這個索引就變成了主索引了。所以InnoDB 存儲引擎中的主索引只有一個,其余的索引都作為輔助索引。

​ 在根據主索引搜索時,直接找到key所在的葉子節點即可取出數據;輔助索引是一種非聚集索引,其在葉子節點中存儲的數據是索引列所在的表中對應的主鍵值。所以在根據輔助索引(二級索引)查找時,則需要先搜索輔助索引取出主鍵的值,然后依據取出的主鍵查詢主索引,通過主鍵值找到數據,一共查詢了兩次。

​ 如圖所示,以某個數據表的name建立的輔助索引的結構圖:

聚簇索引

​ 聚簇索引是一種索引結構和數據存放在一起的索引,數據庫表行中數據的物理順序與鍵值的邏輯(索引)順序相同。一個表只能有一個聚集索引,因為一個表的物理順序只有一種情況,所以對應的聚集索引只能有一個。如果某索引不是聚集索引,則表中的行物理順序與索引順序不匹配,與非聚集索引相比,聚集索引有着更快的檢索速度。

覆蓋索引

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

覆蓋索引即需要查詢的字段正好是索引的字段,那么直接根據該索引,就可以查到數據了, 而無需回表查詢。所以對於非聚簇索引來說,並不是必須要回表查詢的,只要查詢的字段正好是索引的字段,那么就可以直接返回查詢的結果,無需再根據主鍵值到主鍵索引中再次查詢了。

索引優化

1、對於InnoDB存儲引擎數據表來說,盡量使用自增主鍵。

​ InnoDB使用聚集索引,數據記錄本身存放在主索引(B+樹)的葉子結點上,這就要求同一個葉子結點(大小為一個內存頁或磁盤頁)的數據記錄按主鍵順序存放,每當一條新的記錄插入時,mysql會根據其主鍵將其插入適當的節點和位置,如果頁面達到裝載因子(InnoDB默認為15/16),則開辟一個新的頁(節點)。 如果使用自增主鍵,那么每次插入新的記錄,記錄就會順序插入到當前節點的下一個位置。這樣就會形成一個緊湊的索引結構,每次插入不需要移動已有數據,因此效率很高。

​ 如果使用非自增主鍵(例如身份證號或學號這種無序字符串),每次插入主鍵近似隨機,每次記錄都要插入到現有索引頁的中間的某個位置,這時不得不移動元素來完成插入,增加了開銷。因此,在設計表的時候,不建議使用過長的字段作為主鍵,也不建議使用非單調的字段作為主鍵,這樣會造成主索引頻繁分裂。

2、最左匹配原則

聯合索引:mysql可以將多個列按照順序作為一個索引,這種索引叫做聯合索引。

索引的最左匹配原則是:假如索引列分別為A,B,C,順序也是A,B,C,那么:

  • 查詢的時候,如果查詢【A】,【A,B】,【A,B,C】,可以使用索引查詢。
  • 如果查詢的時候,查詢【A,C】,由於中間缺失了B,那么C這個索引是用不到的,只能用到A索引。
  • 如果查詢的時候,查詢【B】,【B,C】或【C】,由於缺失了最左前綴A,那么是用不到這個聯合索引的,除非有其他索引。
  • 如果查詢的時候使用范圍查詢,並且是最左前綴,那么可以用到索引,但是范圍后面的字段無法用到索引。

這個原則可以結合索引的原理來理解:Mysql索引是B+樹這種復合結構,當索引是聯合索引,比如【name,age,sex】時,B+樹是按照從左到右的順序建立索引樹的。當(張三,20,M)這樣的數據來檢索時,B+樹會優先根據name來確定下一步的搜索方向,如果name相同再比較name和sex,最后得到檢索的數據。但當(20,M)這樣的數據來的時候,mysql就不知道該查哪個節點,因為建立索引的時候,name就是第一個比較因子,必須先根據name去確定下一步去哪里搜索。當(張三,M)這樣的數據來時,可以根據name是“張三”,來確定下一步的搜索,然后再去匹配性別是“M”的數據,因此只能用到聯合索引中name這個索引。

3、其他原則

1.選擇合適的字段創建索引:

  • 不為 NULL 的字段 :索引字段的數據應該盡量不為 NULL,因為對於數據為 NULL 的字段,數據庫較難優化。如果字段頻繁被查詢,但又避免不了為 NULL,建議使用 0,1,true,false 這樣語義較為清晰的短值或短字符作為替代。
  • 被頻繁查詢的字段 :我們創建索引的字段應該是查詢操作非常頻繁的字段。
  • 被作為條件查詢的字段 :被作為 WHERE 條件查詢的字段,應該被考慮建立索引。
  • 頻繁需要排序的字段 :索引已經排序,這樣查詢可以利用索引的排序,加快排序查詢時間。
  • 被經常頻繁用於連接的字段 :經常用於連接的字段可能是一些外鍵列,對於外鍵列並不一定要建立外鍵,只是說該列涉及到表與表的關系。對於頻繁被連接查詢的字段,可以考慮建立索引,提高多表連接查詢的效率。

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

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

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

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

4.注意避免冗余索引

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

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

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

6.索引列在sql語句中不能參與運算,否則會導致索引失效。

​ 例如from_unixtime(create_time) = ’2014-05-29’就不能使用到索引,原因很簡單,b+樹中存的都是數據表中的字段值,但進行檢索時,需要把所有元素都應用函數才能比較,顯然成本太大。應該改成create_time = unix_timestamp(’2014-05-29’);

參考:

MySQL | 索引 | MyISAM存儲引擎的索引 和 InnoDB存儲引擎的索引(圖文詳解)_ThinPikachu的博客-CSDN博客

JavaGuide (gitee.io)


免責聲明!

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



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