面試MySql索引
本博文是觀看了某個機構的視訊之後做了總結。
一.丟擲問題:為什麼需要索引?
作業系統層面引出索引
假設我們建立這樣一張表
CREATE TABLE `person` (
`id` int(11) NOT NULL,
`name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Person 表中有兩個欄位:id和name.給表中加資料
我們知道資料庫的資料最終它都會儲存在檔案中。那麼我們查詢id為1的資料時,我們會這麼寫:SELECT * FROM person WHERE ID = 1.
如果沒有索引,我們怎麼查詢呢,我想我們只能讀取檔案,逐行掃描,找到符合的結果。如果 我們再來查詢:
SELECT * FROM person WHERE ID = 3,那麼有需要讀取檔案,逐行掃描,這樣子效率會特別差。這時候就需要有一種東西可以幫助我們實現高效的獲取資料。索引就這麼出現了。在討論索引之前,還要討論一下底層檔案系統是怎麼樣進行讀取,寫入資料的。
如圖一,在檔案系統中,table表是這麼儲存的,有一個檔名,檔名對應著一個三元組(柱面,磁軌,扇區),當我們要去讀機械硬碟時,我們需要通過我們的柱面,磁軌定位到扇區,然後找到table.idb儲存在圖右磁碟的紅色區域內,讀取資料時(假設順時鐘),將會從A依次讀到B,這就是原始儲存和讀取方式。所以當我們每次查詢的時候都是需要如圖2 中從A到B的掃描讀取,效率很低。
索引出現,解決效率低問題
這時候設計索引(假設表是以ID為索引),那麼就可以通過這個索引快速的定位到磁碟中的某一個位置,如上圖。
二,索引採取什麼樣的結構
我們知道幾種適合快速查詢的資料結構,比如:hash(雜湊)、二叉樹,紅黑樹,B+樹。
而mysql的索引資料結構採取的是B+樹。那麼為什麼不採用別的呢,以下依據索引優略的標準(IO漸進複雜度)做個比較。
1.hash(雜湊)
如果使用雜湊作為索引,對ID做索引的話,可以對ID使用hash(ID),儲存到合適的位置,這樣在使用sql查詢,如:
select * from person where id = 1,就可以對id做hash,快速的找到資料。這樣是沒錯,可是使用hash的話,有一個顯著的弊端,無法進行範圍的查詢。比如:select * from person where id > 1;這樣使用雜湊完全沒有辦法找到資料在哪裡。
2.對比 二叉樹,紅黑樹,B+樹
先上圖:
題外話:這個圖可以通過一個學習網站自己畫出來 B+樹、二叉樹、紅黑樹
這裡插入10個數據。
我們說一個索引的是否優秀判斷的依據是 IO漸進複雜度,那麼我們討論一下三種結構,很明顯二叉樹的樹高要遠遠大於 紅黑樹 和 B+樹的樹高,而且一個節點只能放一個數據。假如一個數據節點就需要一個IO,有千萬個數據節點的話,那麼二叉樹就需要千萬次IO,那麼這個效能就不行了。如果用紅黑樹來做索引的話,比二叉樹相對來說會好一些。它會有一個因子會控制樹高,但是一旦資料很多,這個樹高就是不可控了。而相對於B+樹,它的樹高是恆定(可以控制在3到5層),也就是說它的漸進複雜度是恆定的。(具體比較各種樹,請自行找資料,我對樹的資料結構也不是很瞭解)
三.mysql中的索引
這裡討論兩種mysql的儲存引擎:MyISAM和InnoDB。
在資料庫儲存檔案中 MyISAM儲存引擎的mysql對錶會儲存三個檔案,字尾名 MYI(索引檔案),字尾名MYD(資料檔案),字尾名frm(表字段結構檔案)。而對於InnoDB儲存引擎的表會儲存兩個檔案,字尾名frm(表字段結構檔案),字尾名ibd(資料檔案)。可見InnoDB儲存引擎下的表沒有索引檔案,為什麼呢?它又是怎麼儲存的呢?
在瞭解兩種引擎如何設計索引之前,先來了解一下,BTree和B+Tree的區別。
由圖1 和 圖2 可以知道,BTree每個節點都可以儲存資料,而B+Tree只有葉子節點才儲存資料,第二B+Tree在mysql的實現時還做了定製,可以看到相鄰的葉子節點間加了鏈式的關聯的。這樣的優勢是在進行範圍查詢時會很快,比如:執行
select * from person where id > 1 . 這樣就可以找到1的葉子節點,然後順著鏈式關聯就可以很快速的找下去。
接著看 MyISAM 是如何設計索引的。我們知道MyISAM儲存表時會有三個檔案(索引檔案,資料檔案,欄位表結構檔案)。那麼查詢時怎麼利用索引檔案和資料檔案來查詢資料的呢。用以下的一張圖可以表現出來。
可以看見左邊是MYI檔案,右邊是MYD檔案(上圖是建立以ID為索引的)
當我們查詢 select * from table where id = 1的時候,它會判斷id是否建立索引,如果建立索引,就會到MYISAM檔案中定位到1的位置,找到對應的邏輯地址,然後去MYD檔案中去找地址上儲存的資料。如下圖
上列是基於ID建立索引,再來看看基於name建立索引。
從上圖可以看出以name為索引的MYI檔案,它的二元組也儲存的是邏輯地址。當我們執行select * from table where name = 'james'時,它也會判斷有沒有對於name建立索引,進而從MYI表中找到james為key對應的邏輯地址。再從MYD檔案中找到資料。
以上這種索引叫非聚集索引
接下來看看 InnoDB儲存引擎下的 索引(聚集索引 -- 以索引來組織資料)
在InnoDB儲存引擎下只有兩個檔案,沒有MYI檔案,那麼它怎麼實現索引的呢?看下圖,InnoDB是以主鍵為索引來組織資料的。
可以看到InnoDB組織的資料就是一顆B+Tree(也可以理解為索引檔案和資料檔案在一起),而不是像MyISAM中MYD檔案的儲存方式,所以它不需要類似MYI的索引檔案。
比如上圖就是以ID為主鍵建立索引組織的資料。如果是以name為主鍵建立索引的呢?既然需要以主鍵建立索引,那麼InnoDB是否允許建立表時不設定主鍵呢?其實InnoDB底層是這樣選擇的,如果建立表時沒有指定主鍵,InnoDB會為你指定一列不重複資料的列作為主鍵,如果找不到這樣的列,那麼InnoDB會給你生成一列作為主鍵(相當於Mongodb 中的 objectId策略)。
如果我們執行 select * from table where id = 1時,它會根據ID主鍵為索引找到如上圖1節點的位置,然後直接返回資料了。
如果我們還要把name也建立一個索引,那麼在執行 select * from table where name = 'james' 時會怎麼樣呢,如下圖,如果以name建立一個副索引,那麼在IDB檔案裡在儲存一個如下圖的結構,葉子節點儲存的是主鍵。
那麼我們在執行 select * from table where name = 'james'時,會先判斷name是否建立索引,那麼會在索引樹種找到james,拿到儲存的主鍵值,在通過主鍵去那顆以主鍵為索引組織的資料的樹種去找到主鍵的位置,從而拿到資料。過程圖下圖:
以上就是這篇博文要討論的地方 ,謝謝您的觀看,如果可以請留下您寶貴的意見~