索引簡介
索引通過將無序的數據變成相對有序的數據來提高查詢速度。
使用索引的好處:
通過創建唯一性索引,可以保證數據庫表中的每一行數據的唯一性。可以大大加快數據的檢索速度將隨機IO變為順序IO(順序IO不需要多次磁盤尋道,所以比隨機IO快很多,當對于查詢時無需再做排序了)可以加速表與表之間的連接,特別是在實現數據的參考完整性方面
注:
當創建索引后,當對表中數據進行增加、刪除和修改的時候,索引也要進行動態維護,降低了數據的維護速度。
索引需要占用物理空間,除了數據表占用數據空間以外,每一個索引還要占用一定的物理空間,如果要簡歷聚簇索引、那么需要的空間就會更大
創建索引和維護索引都要耗費時間,這種時間隨著數據量的增大而增大。
索引使用場景:
在經常需要搜索的列上使用索引可以加快搜索的速度
在經常使用where子句的列上創建索引,加快條件的判斷速度
在經常需要排序的列上使用索引非常有效,但是對于特大型表的維護開銷非常大mysql數據庫索引有哪些,不適合創建索引。
在經常使用連接的列上,這些列都是一些外鍵,可以加快連接的速度。
以下場景避免使用索引:
避免where子句中對字段施加函數,會造成無法命中索引。
在使用時,使用與業務無關的自增主鍵作為索引,即邏輯主鍵,而不要使用業務主鍵
將打算加索引的列設置為not null,否則將導致引擎放棄使用索引而進行全表掃描
刪除長期未使用的索引,不用的索引會造成不必要的性能消耗
在使用limit 查詢緩慢時,可以借助索引來提高性能
避免使用冗余索引:若索引的功能相同,能夠命中就一定能夠命中,如(name,city)和(name)就是冗余索引,因此一般應該擴展已有的索引,而不是創建新的索引。
覆蓋索引:
如果一個索引包含所有需要查詢的字段的值,我們就稱之為覆蓋索引。在存儲引擎中,如果不是主鍵索引,葉子節點存儲的是主鍵+列值,最終還是要回表,也就是通過主鍵再找一次,這樣會比較慢,而覆蓋索引就是把要查詢的列和索引是對應的,不做回表操作。
最左前綴原則:MySQL可以以一定的順序引用多列,這種索引叫做聯合索引,如果查詢時條件精確匹配索引的左邊連續一列或幾列,(如果跳過左邊的列對右邊的列進行匹配,則無法命中,但是如果只是順序不同,但是查詢時索引中的列都用上了,查詢引擎會自動優化查詢順序)則此列就可以被用到。因此列的排列順序決定了可命中索引的列數。
索引的數據結構
MySQL的基本存儲結構是頁(記錄都存在頁里面),各個數據頁可以組成一個雙向鏈表,每個數據頁都會為存儲在它里面的記錄生成一個頁目錄,每個數據頁中的內容又可以組成一個單向鏈表。
數據頁和頁目錄的關系圖如下:
通過主鍵查找記錄的過程:
從第一個頁開始,沿著雙向鏈表對每一個頁進行查找,每個數據頁都會為存儲在它里面的記錄生成一個頁目錄,在通過主鍵查找某條記錄的時候可以在頁目錄中 使用二分法快速定位到對應的槽,然后遍歷該槽里面對應分組的記錄就可以快速找到指定的記錄。
普通查詢過程:
當我們查詢一個沒有任何優化的sql語句時,會先遍歷雙向鏈表,定位到所在的頁,由于不是根據主鍵查詢,因此會從最小記錄開始一條一條遍歷頁所在的單鏈表中的每條記錄,然后對比每條記錄是不是復合搜索條件。
表的存儲結構
記錄頭信息里的屬性,它的各個取值代表的意思如下:
0:普通的用戶記錄
1:目錄項記錄
2:最小記錄
3:最大記錄
當我們使用主鍵索引時,存儲引擎會按照主鍵的大小,將頁內的記錄按照順序排成一個單向鏈表
然后按照每一個頁中記錄的最大主鍵值對頁進行排序成為一個有序雙向鏈表,要求下一個頁中的主鍵值必須大于上一個頁中的主鍵值,因此在雙向鏈表中,頁號基本是不連續的。
為了能快速定位到頁,通過對每個頁增加了一個記錄,這個記錄只包含兩個值,分別是:頁的用戶記錄中最小的主鍵值key,和頁號
然后采用頁的方式,將這些頁記錄順序連接成為一個單向鏈表放在一個叫目錄項記錄的頁中,目錄項記錄頁通過對頁記錄的主鍵值生成一個Page (頁目錄)以加快在頁內的查詢速度,通過這些主鍵值,再將這些目錄項記錄順序連接為一個雙向鏈表。
如果數據非常龐大,就會通過逐級生成目錄項紀錄頁,直到得到一個頂層目錄頁。這個就是B+樹。因此我們的實際用戶記錄其實都存放在B+樹的最底層的節點上。
存儲引擎會自動的為我們創建聚簇索引,聚簇索引的特點是:
1.使用記錄主鍵值的大小進行記錄和頁的排序,這包括三個方面的含義:
2.B+樹的葉子節點存儲的是完整的用戶記錄。
回表
當我們查詢主鍵時會采用上述的B+樹結構內容進行查詢,如果是查詢其他列,那么就會為相應的列也同樣創建一個這樣的B+樹。
但是頁內的記錄是按照指定列的大小順序排成一個單向鏈表。
各個存放用戶記錄的頁也是根據頁中記錄的指定列大小順序排成一個雙向鏈表。
各個存放目錄項的頁也是根據頁中記錄的指定列大小順序排成一個雙向鏈表。
在這個B+樹的葉子節點存儲的并不是完整的用戶記錄,而只是指定列+主鍵這兩個列的值,所以我們必須再根據主鍵值去聚簇索引中再查找一遍完整的用戶記錄,這個過程也被稱為回表。
所以這種B+樹也被稱為二級索引(英文名 index),或者輔助索引。由于我們使用的是c2列的大小作為B+樹的排序規則,所以我們也稱這個B+樹為為c2列建立的索引。
我們也可以為多個列創建索引,存儲引擎會先對最左邊的列的記錄和頁進行排序,如果在最左邊列的記錄相同的情況下,會對右邊列進行排序,這就是為什么最左前綴原則中對左邊連續一列或幾列進行精確匹配時才可以命中的原因。
目前大部分數據庫系統及文件系統都采用B-Tree或其變種B+Tree作為索引結構
B-樹
B-樹是一種多路搜索樹,其特點是:
B-樹的搜索:從根結點開始,對結點內的關鍵字(有序)序列進行二分查找,如果命中則結束,否則進入查詢關鍵字所屬范圍的兒子結點;重復,直到所對應的兒子指針為空,或已經是葉子結點;
B+樹
B+樹是B樹的一個變種,mysql使用的就是B+樹實現索引結構
B+樹的特點有:
索引一般以文件形式存儲在磁盤上,索引檢索需要磁盤I/O操作,由于存儲介質的特性,磁盤本身存取就比主存慢很多,再加上機械運動耗費,磁盤的存取速度往往是主存的幾百分分之一,因此為了提高效率,要盡量減少磁盤I/O。為了達到這個目的,磁盤往往不是嚴格按需讀取,而是每次都會預讀,即使只需要一個字節,磁盤也會從這個位置開始,順序向后讀取一定長度的數據放入內存。
在數據庫中,B-Tree的一個節點的大小設為等于一個頁,這樣每個節點只需要一次I/O就可以完全載入。
索引的實現:
中索引即數據,也就是聚簇索引的那棵B+樹的葉子節點中已經把所有完整的用戶記錄都包含了,而的索引方案雖然也使用B+樹,但是卻將索引和數據分開存儲:
將表中的記錄按照插入時間順序的存儲在一塊存儲空間上,我們可以通過行號而快速訪問到一條記錄
會單獨為表的主鍵創建一個B+樹索引,只不過在B+樹的葉子節點中存儲的不是完整的用戶記錄,而是主鍵值 + 行號的組合。也就是先通過索引找到對應的行號,再通過行號去找對應的記錄,因此在中對主鍵查詢需要進行一次回表操作mysql數據庫索引有哪些,意味著中建立的索引全部都是二級索引