:接觸mysq也有兩年左右的時間了,可是對該數據庫的理解自認還比較初級,看過不少文章,也看過一些相關的書籍,依然小白。。。。(這裏我的總結是兩點主要緣由:1.對mysql的學習大部分都是源於看一些雜七雜八的文章,不少文章自己表述有誤或不許確。2.實操較少,平時增刪改查索引的使用很常規,缺乏鑽研精神 3.第三點也就是寫這篇文章的主要目的,看到的東西常常是看了就看了,過一段時間就忘記了,而後反覆重複這樣的經歷,時間花的很多,可是這樣永遠不會對mysql系統掌握,故而打算經過文章把對mysql的理解落地,理解多少寫多少,可能會東一點西一點比較亂,往後逐漸完善吧)html
對索引的認識:mysql
什麼是索引?sql
我最初的理解是把索引當場一本字典的目錄。數據庫
假如一本字典沒有目錄,那咱們想查到某個字只能從頭找到尾。數組
因此纔會對字典按照頁碼來進行劃分,而且在字典的最開始幾頁,有相應的目錄可讓咱們知道目標字在哪一頁,從而快速定位到目標。數據結構
並且根據需求的不一樣,通常用過字典的小夥伴都知道,有按照拼音開頭的目錄,有按照偏旁部首開頭的目錄,等等。。。 其實和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存儲引擎,兩者對索引的實現分別爲非彙集索引(非聚簇索引)和彙集索引(非聚簇索引),這也是最多見的兩種索引的實現
MyISAM引擎使用的是非彙集索引的形式,簡單來講是索引和數據是分開存放的,索引存放的是數據文件的地址,咱們須要先找到索引,而後再經過索引找到數據
在MyISAM引擎中,主鍵和通常索引在結構上沒有區別,只是主鍵必須是惟一的,可是在查詢時,普通索引和主鍵索引都是須要先得到數據文件地址,再去找到相應的數據
該存儲引擎使用的是彙集索引,該索引方式最明顯的特色就是主鍵索引和數據是在一塊兒的(同一個文件中),能夠理解爲找到了主鍵索引也就找到了數據,不須要二次查找
而該索引結構當中的普通索引,想找到數據,也必須經過主鍵索引進行二次查找才能夠
即:經過主鍵索引查找數據,查找一次 ,而經過普通索引查找數據,則須要普通索引找到主鍵索引,經過主鍵索引找到數據,也就是所謂的二次查找
如圖爲:mysql中兩種索引方式的對比
目前使用的存儲引擎最多的就是InnoDB,咱們的索引形式通常也爲彙集索引,也能夠稱之爲主鍵索引,如下討論的索引使用策略基於該存儲引擎
一、最左前綴原理:以聯合索引舉例比較容易理解:通常來講,聯合索引即多個字段共同組成的索引,如 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';
二、範圍查詢只能用到一次索引
三、 在查詢條件中含有表達式或者函數,則沒法用到索引,理由很簡單,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