面試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,拿到存儲的主鍵值,在通過主鍵去那顆以主鍵爲索引組織的數據的樹種去找到主鍵的位置,從而拿到數據。過程圖下圖:



以上就是這篇博文要討論的地方 ,謝謝您的觀看,如果可以請留下您寶貴的意見~


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章