前言
公司最近組織開展一系列的技術分享會,我借着這次機會把自己以前學的一些知識點重新歸納一下記錄起來。首先是對自己技術的積累有好處,其次也是想讓自己學習的知識能夠更深入的理解,有不足或者錯誤的地方歡迎指出,共同進步。
探究一下MYSQL索引為什么這么快?索引究竟是什么?
1.什么是索引?
- MYSQL官方文檔介紹索引是一種方便快速查詢數據的數據結構。用我們生活中的例子來講,索引就好比書的目錄,如果沒有目錄,每次你想要查找某些內容,你必須從頭開始查找,這樣的效率極其低下。
- 索引一般比較大,所以大部分情況下索引是存在磁盤的索引文件上,也有可能是存在數據文件上。
- 索引的種類有很多:主鍵索引(這是最常見的一種索引,主鍵不能為空且必須唯一)、唯一索引(相對於主鍵索引,它的值可以為空)、全文索引(在char、varchar、text類型可以使用)、普通索引、前綴索引。按照列數來區分:單一索引、組合索引(多字段組成)
2.MYSQL索引的數據結構
在講解MYSQL索引的數據結構之前,我們先看看了解一下其他的數據結構,看看他們的優缺點進行對比。
2.1 二叉樹
二叉樹簡單來說就是左節點大於右節點,在理想的情況下,他的查找速度就接近與二分法的性能O(log2n)。因為在內存排序的時間是非常快的,可以忽略不計,所以總的消耗時間就取決於IO的操作次數。二叉樹查找速度取決樹高,每次查詢接口都是一次IO操作,也是性能的瓶頸所在。
但是也會有這種一種情況,同樣也是二叉樹,但是他的樹非常高,導致查詢一次需要多次IO操作,效率及其低下
2.2 平衡二叉樹
平衡二叉樹可以解決二叉樹不穩定導致查詢效率低下的缺點。平衡二叉樹的特點:樹的左右節點層級最高相差一層。在插入或者刪除的情況下,通過左旋轉或右旋轉使得整個二叉樹平衡,不會出現層級相差很多的情況。平衡二叉樹的性能接近二分法查找O(log2n)。
平衡二叉樹查找id為8的記錄,只需要IO操作2次即可。但是仔細想一下,如果數據量很多呢?假設數據表有100W的數據,根據O(log2n)計算,大約需要20次IO操作。磁盤尋道大概需要10ms,總的查詢時間為20 * 10 = 0.2,效率也比較低下。
還有就是平衡二叉樹不支持范圍查詢,范圍查詢每次都需要從根節點遍歷,效率及其低下。
2.3 B-樹(改造二叉樹成多叉樹)
之前的幾種樹形結構適合與小數據量的內存查找,也叫做內查找。在1970年,R.Bayer和E.Mccreight提出了一種適合於外查找的平衡多叉樹B-樹。MYSQL數據文件是存在磁盤的,每次都是按照一頁大小(一般而16K)讀取內存。像二叉樹、平衡二叉樹,每次讀取節點都要進行一次IO操作,所以樹越高IO操作次數越多。想要提高查詢效率,首先要解決的就是降低樹高的問題。
設想一下,每一次IO操作讀取一個節點,讀取16K大小的內存數據,但是每次節點的數據實際上遠遠小於16K。假設節點數據大小為16B,為了讓一次IO操作能夠讀取更多節點,我們可以在每個節點盡可能地存儲索引數據。我們在每個節點存儲1000個索引數據(1000*16B = 16K),將二叉樹改造成多叉樹,從樹高變成樹“胖”,解決了樹高的問題,從而降低IO操作次數,提高查詢效率。
B-樹的特點:1.每個節點存儲多個元素 2.節點的元素包含鍵值以及數據 3.所有葉子節點存放同一層,具有相同深度,葉子節點之間沒有指針連接。這種數據結構解決了樹高IO次數多的問題,但是在每個節點存儲數據,假設數據一旦很大,每個節點儲存的索引數也隨之減少,最后還是會導致樹很高,查詢效率低。其次,B-樹不能范圍查詢。設想一下如果我們要查詢15-25范圍內的數據,查到15之后就又要重新回到根節點繼續查找,這樣循環遍歷的效率有待提高。
2.4 B+樹(改造B-樹)
結合了B-樹的缺點進行改造,就誕生了B+樹。B+樹跟B-樹的差異並不是很大,判斷的依據很簡單:節點是否存放數據。B+樹存放數據的節點只有葉子節點,而且葉子結點雙向指針連接,形成了雙向有序鏈表。
這樣一來,除了葉子節點其他存放的都是索引鍵值,可以很大程度增加節點存放索引樹,從理論上樹是要比B-樹“矮”的。同時B+樹支持范圍查詢,因為底層葉子節點是雙向有序鏈表,而且主鍵具有唯一性(對於輔助索引后面會講到),假設范圍為15到19,我們只需要查到15記錄之后繼續往后查詢,直到大於19即可,無需從根節點再次遍歷,效率較高。
3.MYSQL索引B+樹實踐
MYISAM引擎 (主鍵索引)
MYISAM引擎是非聚簇索引,也就是說B+樹的葉子節點的鍵值存放索引列的值,數據存在數據在磁盤的地址。MYISAM的索引文件跟數據文件是分開存儲的。
CREATE TABLE
student(
id int(11) NOT NULL AUTO_INCREMENT,
namevarchar(20) DEFAULT NULL,
age int(11) DEFAULT NULL, PRIMARY KEY (
id) USING BTREE, KEY
idx_age (
age) USING BTREE ) ENGINE = MyISAM AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8;
創建了一個表student,id為主鍵索引,age為普通索引。假設表中有以下數據,現在執行以下語句
SELECT * FROM stduent WHERE id = 16
具體鏈路:(實際上邏輯上相鄰實際磁盤並不一定相鄰,這里只是方便展示)
(1)先從磁盤1加載數據到內存,因為18>16走左路(一次IO操作) (2)讀取磁盤2加載數據到內存,又因為16>14向下繼續讀取(一次IO操作) (3)檢索葉子節點,判斷到等於16則停止(一次IO操作)
MYISAM引擎 (輔助索引)
在MYISAM引擎中,主鍵索引跟輔助索引的差別並不很大,葉子節點存放的都是磁盤地址,只是輔助索引並並不是唯一值,所以在等值查詢檢索葉子節點的時候,也要按照范圍一樣,進行檢索數據。
Innodb引擎 (聚簇索引、主鍵索引)
Innodb引擎使用的是聚簇索引。每一個數據表都有一個聚簇索引,采用B+樹的數據結構,葉子節點鍵值對應存放的是整行數據記錄。在Innodb中,非聚簇索引就是輔助索引,葉子節點存儲的數據是主鍵值。如果一個表沒有主鍵,innodb引擎會自動構建一個隱藏的rowid在構成聚簇索引。依舊是按照我們剛剛講解MYISAM引擎的數據表例子:
SELECT * FROM stduent WHERE age = 37
具體鏈路:(實際上邏輯上相鄰實際磁盤並不一定相鄰,這里只是方便展示)
(1)先從磁盤1加載數據到內存,因為18<37走左路(一次IO操作) (2)讀取磁盤2加載數據到內存,又因為37>24向下繼續讀取(一次IO操作) (3)檢索葉子節點,判斷到等於37則停止(一次IO操作) (4)這時候查到的數據就是age字段為37的記錄主鍵值。按照聚簇索引的方式再查找數據就得到了數據結構集(這個過程叫做回表)相同索引字段情況下,按主鍵字段排序。因為要多加上三次回表操作,效率回相對低一點點。這里有個概念叫做覆蓋索引,如果查詢所需要的字段剛好就是索引字段就不需要回表查詢,從而提高了查詢效率。
總結
索引的原理遠遠不止於這么一點點,組合索引以及一些其他的原理我暫時理解還不是到位,等到后面學習更加理解之后再寫一篇文章進行記錄總結吧。“學而不思則惘,思而不學則殆”,以前沒辦法理解這句話的涵義,直到后來才知道總結、思考才是學習最有效率的方式。多總結、多思考,也是作為一名程序員進步的最快方式。