初識mysql索引 - 小白篇


:接觸mysq也有兩年左右的時間了,但是對該數據庫的理解自認還比較初級,看過很多文章,也看過一些相關的書籍,依然小白。。。。(這里個人總結是兩點主要原因:1.對mysql的學習大部分都是源於看一些雜七雜八的文章,很多文章本身表述有誤或不准確。2.實操較少,平時增刪改查索引的使用很常規,缺少鑽研精神 3.第三點也就是寫這篇文章的主要目的,看到的東西經常是看了就看了,過一段時間就忘記了,然后反復重復這樣的經歷,時間花的不少,但是這樣永遠不會對mysql系統掌握,故而打算通過文章把對mysql的理解落地,理解多少寫多少,可能會東一點西一點比較亂,日后逐漸完善吧)

 

對索引的認識:

什么是索引?

我最初的理解是把索引當場一本字典的目錄。

假如一本字典沒有目錄,那我們想查到某個字只能從頭找到尾。

所以才會對字典按照頁碼來進行划分,並且在字典的最開始幾頁,有相應的目錄可以讓我們知道目標字在哪一頁,從而快速定位到目標。

而且根據需求的不同,一般用過字典的小伙伴都知道,有按照拼音開頭的目錄,有按照偏旁部首開頭的目錄,等等。。。 其實和mysql中的 主鍵索引 聯合索引 等等種類的索引是異曲同工的。

 

數據結構:

mysql索引使用的數據結構是B+樹,目前大部分數據庫系統都采用 B+Tree 或 B-Tree 這兩種數據結構,本文不打算詳解其中原因(對兩種數據結構細節感興趣的可以參考其他文章)

簡單來說,索引文件一般是存在於硬盤當中,由於數據量大無法全部一次加載進內存。而磁盤的IO讀取代價相比於內存讀取代價要高很多倍,所以我們在選擇數據結構上面,最重要的一點就是盡可能減少對磁盤io的操作。

數組:索引文件無法一次加載進內存

鏈表:查詢需要從頭到尾的查詢

上面的兩種B樹特點決定了他們可以保證盡可能少的進行io操作,而且相對於平衡二叉樹、二叉搜索樹等數據結構,樹的高度要低很多,查詢對應的io次數更少。

 

而B+Tree和B-Tree的區別主要是前者的非葉子節點不存儲數據,目的是能夠存儲更多的索引,保證整顆樹更“矮”,但是想要找到數據必須進行樹的高度次IO操作(即B+Tree的高度為3,則需要進行3次IO操作,在葉子節點上找到目標數據)

而B-Tree不論葉子節點還是非葉子節點都存放數據,好處是可能在沒有到達葉子結點的時候就找到了目標數據

但是B-Tree如果想要存放和B+Tree相同量的索引,則必須讓樹的高度增加,也就是增加io次數,所以B+Tree相對更穩定

 

聚集索引和非聚集索引:

在mysql當中,不同的存儲引擎對索引的實現是不同的,本文介紹常見的兩種mysql存儲存儲引擎對索引的實現,即MyISAM存儲引擎和InnoDB存儲引擎,二者對索引的實現分別為非聚集索引(非聚簇索引)和聚集索引(非聚簇索引),這也是最常見的兩種索引的實現

1、MyISAM索引實現:

MyISAM引擎使用的是非聚集索引的形式,簡單來說是索引和數據是分開存放的,索引存放的是數據文件的地址,我們需要先找到索引,然后再通過索引找到數據

MyISAM引擎中,主鍵和一般索引在結構上沒有區別,只是主鍵必須是唯一的,但是在查詢時,普通索引和主鍵索引都是需要先獲得數據文件地址,再去找到相應的數據

 

2、InnoDB索引實現

該存儲引擎使用的是聚集索引,該索引方式最明顯的特點就是主鍵索引和數據是在一起的(同一個文件中),可以理解為找到了主鍵索引也就找到了數據,不需要二次查找

而該索引結構當中的普通索引,想找到數據,也必須通過主鍵索引進行二次查找才可以

即:通過主鍵索引查找數據,查找一次 ,而通過普通索引查找數據,則需要普通索引找到主鍵索引,通過主鍵索引找到數據,也就是所謂的二次查找

如圖為:mysql中兩種索引方式的對比

 

 

 索引的使用策略,以及需要注意的點:

目前使用的存儲引擎最多的就是InnoDB,我們的索引形式一般也為聚集索引,也可以稱之為主鍵索引,以下討論的索引使用策略基於該存儲引擎

1、最左前綴原理:以聯合索引舉例比較容易理解:一般來說,聯合索引即多個字段共同組成的索引,如 student表中:<stu_num, stu_age, date>學號 年齡  日期,三個字段建立的聯合索引

    我們在查詢時正常來說也必須按照建立聯合索引的順序來進行查找  如:

select * from student where stu_num = 1 and stu_age = 22 and date = '1993-11-11'; 

這樣是可以充分利用聯合索引的

但是由於mysql會在查詢時對我們的sql語句進行優化 ,所以即便是我們寫的順序是亂的,mysql依然會把sql調整為正確順序 

select * from student where stu_age = 22 and stu_num = 1  and date = '1993-11-11'; 

如果我們只用聯合索引當中的某個字段來查詢時,只能用到聯合索引的一部分

select * from student where stu_num = 1

但是如果我們用到的字段中間斷開了,即缺少中間的某個字段,則后面的data列的索引無法觸發

select * from student where stu_num = 1 and date = '1993-11-11'; 

如果我們沒有用到第一列的索引,則不會觸發任何索引

select * from student where stu_age = 22 and date = '1993-11-11'; 

 

2、范圍查詢只能用到一次索引  

 

3、 在查詢條件中含有表達式或者函數,則無法用到索引,理由很簡單,MySQL還沒有智能到自動優化常量表達式的程度,因此在寫查詢語句時盡量避免表達式出現在查詢中,而是先手工私下代數運算,轉換為無表達式的查詢語句。

 

索引的選擇性:

在我們選擇某一列數據作為索引的時候,有一個標准,稱之為索引選擇性,所謂索引的選擇性(Selectivity),是指不重復的索引值(也叫基數,Cardinality)與表記錄數(#T)的比值

在這里我想通俗的解釋一下。我們建立索引的目的是要在查找的時候盡可能的降低我們的搜索范圍。

比如student數據表中有10000條數據,存儲50個班級的學生,

我們如果不用任何索引來查找一個學生,那我們需要定位的范圍是10000條數據

我們通過學號這個唯一的字段作為索引,我們可以鎖定該數據在某一行。

如果我們通過班級作為索引我們可以鎖定該數據在200條數據以內

如果我們通過性別作為索引,(假設只有男女兩種性別,並且男女比例1:1),我們可以鎖定該數據在5000條數據范圍內。

我們所說的選擇性表達的含義就是這樣,如果在犧牲了大量空間代價建立的索引,在查找時只能將搜索范圍縮小一半,那我們依然需要遍歷5000條數據,索引的意義也就不大。

故而我們在建立索引時,需要盡可能的在查找時縮小查詢范圍

 

是否要選擇與業務無關的自增主鍵作為索引? 

 

在使用InnoDB存儲引擎時,如果沒有特別的需要,請永遠使用一個與業務無關的自增字段作為主鍵。

 

經常看到有帖子或博客討論主鍵選擇問題,有人建議使用業務無關的自增主鍵,有人覺得沒有必要,完全可以使用如學號或身份證號這種唯一字段作為主鍵。不論支持哪種論點,大多數論據都是業務層面的。如果從數據庫索引優化角度看,使用InnoDB引擎而不使用自增主鍵絕對是一個糟糕的主意。

 

 

上文討論過InnoDB的索引實現,InnoDB使用聚集索引,數據記錄本身被存於主索引(一顆B+Tree)的葉子節點上。這就要求同一個葉子節點內(大小為一個內存頁或磁盤頁)的各條數據記錄按主鍵順序存放,因此每當有一條新的記錄插入時,MySQL會根據其主鍵將其插入適當的節點和位置,如果頁面達到裝載因子(InnoDB默認為15/16),則開辟一個新的頁(節點)。

 

如果表使用自增主鍵,那么每次插入新的記錄,記錄就會順序添加到當前索引節點的后續位置,當一頁寫滿,就會自動開辟一個新的頁。如下圖所示:

 

如果我們選擇一個身份證號或者學號之類的字段作為索引,則每次生成的主鍵是隨機的,可能會有大量的葉子節點的裂變,同時維護索引的代價也大大增加

 

 

 

 

參考文獻:http://blog.codinglabs.org/articles/theory-of-mysql-index.html

 

 


免責聲明!

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



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