程序員需要了解的知識點—MySQL索引機制
一、索引是什么
MySQL官方對索引的定義為:索引(Index)是幫助MySQL 高效 獲取數(shù)據(jù)的數(shù)據(jù)結(jié)構(gòu),而MYSQL使用的數(shù)據(jù)結(jié)構(gòu)是:B+樹
在這里推薦大家看一本書,《深入理解計算機系統(tǒng)的書》
1.1 局部性原理
程序和數(shù)據(jù)的訪問都有聚集成群的傾向,在一個時間段內(nèi),僅使用其中一小部分,在最近的將來將用到的信息很可能與現(xiàn)在正在使用的信息在空間地址上是臨近的(稱空間局部性),或者最近訪問過的程序代碼和數(shù)據(jù),很快又被訪問的可能性很大(稱時間局部性)。
1.2 磁盤預(yù)讀
預(yù)讀的長度一般為頁(page)的整數(shù)倍頁是存儲器的邏輯塊,操作系統(tǒng)往往將主存和磁盤存儲區(qū)分割成連續(xù)的大小相等的塊,每個存儲塊稱為一頁(在許多操作系統(tǒng)中,頁大小通常為4K),主存和磁盤以頁為單位交換數(shù)據(jù)
1.3 簡介
在使用數(shù)據(jù)庫中,通常數(shù)據(jù)庫查詢是數(shù)據(jù)庫的最主要功能之一。但每種查找算法都只能應(yīng)用于特定的數(shù)據(jù)結(jié)構(gòu)之上。
- 例如二分查找要求被檢索數(shù)據(jù)有序
 - 而二叉樹查找只能應(yīng)用于二叉查找樹上,但是數(shù)據(jù)本身的組織結(jié)構(gòu)不可能完全滿足各種數(shù)據(jù)結(jié)構(gòu)(例如,理論上不可能同時將兩列都按順序進(jìn)行組織),所以,在數(shù)據(jù)之外,數(shù)據(jù)庫系統(tǒng)還維護(hù)著滿足特定查找算法的數(shù)據(jù)結(jié)構(gòu),這些數(shù)據(jù)結(jié)構(gòu)以某種方式引用(指向)數(shù)據(jù),這樣就可以在這些數(shù)據(jù)結(jié)構(gòu)上實現(xiàn)高級查找算法。這種數(shù)據(jù)結(jié)構(gòu),就是索引。索引一般以文件形式存儲在磁盤上,索引檢索需要磁盤I/O操作。所以評價一個數(shù)據(jù)結(jié)構(gòu)作為索引的優(yōu)劣最重要的指標(biāo)就是在查找過程中磁盤I/O操作次數(shù)的漸進(jìn)復(fù)雜度。
 
- 索引是幫助 MYSQL 高效獲取數(shù)據(jù)的數(shù)據(jù)結(jié)構(gòu)
 - 索引存儲在文件系統(tǒng)中
 - 索引的 文件存儲形式與存儲引擎有關(guān)
 - 索引文件的結(jié)構(gòu):hash、二叉樹、B樹、B+樹
 
二、索引的分類
2.1 hash
這里有一個mysql數(shù)據(jù)文件,有Id和name兩個列,如果我們用hash格式存儲的話(hash表),我們只要計算出某一個列的hash值,把它按照按照數(shù)組的長度取一個模,就可以取到從0-7n個下標(biāo)的位置,這樣的話效率其實是比較高的,但是用hash表存儲,它具備一定的缺點 :
- 利用hash存儲的話需要將所有的數(shù)據(jù)文件添加到內(nèi)存中,比較耗費內(nèi)存空間
 - 如果所有的查詢都是等值查詢,那么hash確實很快,但是在企業(yè)或者實際工作環(huán)境中范圍查找的數(shù)據(jù)更多,而不是等值查詢,因為hash就不太適合了,因此在mysql里面并沒有選擇hash存儲的格式2.2 二叉樹
 
索引格式:

對于樹有他是有一個更新跌過的順序在里面,不要一上來就看結(jié)構(gòu),先是了解什么樹,樹都是由一個樹根,然后有n多個分支組成,這些分支就是一些樹形結(jié)構(gòu),多你有多個樹分支(多元素)的時候,這個時候查找效率就會比較低,因此就有了二叉樹的東西,二叉樹為什么會好用一點,因為二叉樹它是都有兩個分支,但是兩個分支的話,會導(dǎo)致一個效果,就是每次我們在查找數(shù)據(jù)的時候,類似于二分查找的,但是二叉樹也有自己不太好的地方,大家可以看我們上圖中的二叉樹的索引格式,在左邊的節(jié)點會比較短一點(只需要讀三次),而右邊的節(jié)點會長很多(需要讀五次),會導(dǎo)致樹的深度比較深,每一次樹的節(jié)點讀取,都會有一次IO,深度越高,IO越高,會影響我們數(shù)據(jù)讀取的效率,因此也有了(平衡二叉樹)和(紅黑樹)
平衡二叉樹: 維護(hù)一個平衡,就是左子樹和右子樹高度之差,不能大于1,但是對于我們上面的格式就不太適合,因為他已經(jīng)超過1了,但是AVL樹也會有一個問題就是調(diào)整的次數(shù)太頻繁了,它里面涉及到了一個操作就是旋轉(zhuǎn),一種左旋,一個右旋,為了保持平衡需要N多次的旋轉(zhuǎn),這樣的旋轉(zhuǎn)其實是很浪費時間的,每次新增或者刪除的時候,都要經(jīng)歷N多次旋轉(zhuǎn),效率太低了
推薦大家一個網(wǎng)站,可以直接看到AVL樹操作過程,有不了解的同學(xué)可以去看一看,很形象:AVL Trees (Balanced binary search trees)
紅黑樹: 本身也是一個平衡樹,但是它從中間做了一個權(quán)衡,就是損失一部分平衡的性能,但是又保持了相對的平衡,它做了這樣一個操作,就是最長子樹的高度,只要不超過最短子樹的兩倍,就可以了,同時在紅黑樹中它引入了紅和黑兩個節(jié)點信息,有了這些信息它可以幫助我們做一個平衡,在AVL樹有旋轉(zhuǎn)保持平衡,而紅黑樹有了旋轉(zhuǎn)和變色兩種來保持平衡,紅黑樹是AVL樹的進(jìn)階,它損失了一部分平衡的性能,但是維護(hù)了我們插入和刪除數(shù)據(jù)的高效,雖然它損失了一部分性能,但是它依然是一個平衡樹,既然是平衡樹,他最長子樹,不超過最短子樹的兩倍,那意味著如果最短子樹是 4 ,那么最長子樹就是8,這樣在們查找數(shù)據(jù)的時候,又不是一個二分查找了,效率又會變低
無論是二叉樹還是紅黑樹,都會因為樹的深度過深而造成IO次數(shù)變多,影響數(shù)據(jù)的讀取的效率,最重要的就是減少IO
IO是我們IT行業(yè)中的一個瓶頸,一個是磁盤IO一個是網(wǎng)絡(luò)IO,我們作為軟件開發(fā),是沒有辦法去調(diào)整硬件方面的瓶頸,只能從從程序里面減少我們的IO量,我們有兩個方向,一個是減少IO的次數(shù),一個是減少IO的量,從這兩個方面去解決,比如說原來我們讀取數(shù)據(jù)要讀10次,現(xiàn)在只要讀取一次,這樣的IO量就少了10倍,原來我們需要讀1MB的數(shù)據(jù),現(xiàn)在只要讀1KB的數(shù)據(jù),這也就是為什么我們在寫mysql查詢語句的時候不推薦使用select * from ,因為這樣的查詢會查詢到N多個字段,本來我只要兩個字段,但是給了我30個字段,這樣會導(dǎo)致IO量增加了,因此我們就會去考慮,關(guān)于索引的次數(shù)能不能減少,因此下面就引出了我們的——B樹
2.3 B樹
B樹的特點:
- 所有的鍵值分布在整顆樹中
 - 搜索有可能在非葉子結(jié)點結(jié)束,在關(guān)鍵字全集內(nèi)做一次查找,性能逼近二分查找
 - 每個節(jié)點最多擁有m個子樹
 - 根節(jié)點至少有2個子樹
 - 分支節(jié)點至少擁有m/2顆子樹(除根節(jié)點和葉子節(jié)點外都是分支節(jié)點)
 - 所有葉子節(jié)點都在同一層,每個節(jié)點最多可以有m-1個key,并且以升序排列
 
B樹結(jié)構(gòu)說明:
示例圖說明:每個節(jié)點占用一個磁盤塊,一個節(jié)點上有兩個升序排序的關(guān)鍵字和三個指向子樹根節(jié)點的指針,指針存儲的是子節(jié)點所在磁盤塊的地址,兩個關(guān)鍵詞劃分成的三個范圍域?qū)?yīng)三個指針指向的子樹的數(shù)據(jù)的范圍域。以根節(jié)點為列,關(guān)鍵字為16和34,p1指針指向的子樹的數(shù)據(jù)范圍小于16,P2指針指向的子樹的數(shù)據(jù)范圍為16-34,P3指針指向的子樹的數(shù)據(jù)范圍大于34 查找關(guān)鍵字(28)過程:
- 根據(jù)節(jié)點找到磁盤塊1,讀取內(nèi)存【磁盤I/O操作第1次】
 - 比較關(guān)鍵字28在區(qū)間(16,34)找到磁盤塊1的指針P2
 - 根據(jù)P2指針找到磁盤塊3,讀入內(nèi)存【磁盤I/O操作第2次】
 - 比較關(guān)鍵字28在區(qū)間(25,31),找到磁盤塊3的指針P2
 - 根據(jù)P2指針找到磁盤塊8,讀取內(nèi)存,【磁盤I/O操作第3次】
 - 在磁盤塊8中的關(guān)鍵字列表找到關(guān)鍵字28
 
缺點:
- 每個節(jié)點都有key,同時也包含data,而每個頁存儲空間是有限的,如果data比較大的話會導(dǎo)致每個節(jié)點存儲的key數(shù)量變小
 - 當(dāng)存儲的數(shù)據(jù)量很大的時候會導(dǎo)致深度較大,增大查詢時磁盤IO次數(shù),進(jìn)而影響查詢性能
 
2.4 B+樹
B+Tree 是在BTree 的基礎(chǔ)之上做的一種優(yōu)化,變化如下:
- B+Tree 每個節(jié)點可以包含更多的節(jié)點,這個做的 原因有兩個,第一個原因是為了降低樹的高度,第二個原因是將數(shù)據(jù)范圍變?yōu)槎鄠€區(qū)間,區(qū)間越多,數(shù)據(jù)檢索的越快
 - 非葉子節(jié)點存儲key(1,2,3磁盤都是存儲的key),葉子節(jié)點存儲key和數(shù)據(jù)
 - 葉子節(jié)點兩兩指針相互連接(符合磁盤的預(yù)讀特性)順序查詢性能更高如果當(dāng)前磁盤塊下沒有其他節(jié)點,就是 葉子節(jié)點,反之就是 非葉子節(jié)點
 
結(jié)構(gòu)圖:
注意:在B+Tree上有兩個頭指針,一個指向根節(jié)點,另一個指向關(guān)鍵字最小的葉子節(jié)點,而且所有的葉子節(jié)點(即數(shù)據(jù)節(jié)點)之間是一種鏈?zhǔn)江h(huán)結(jié)構(gòu),因此可以對B+Tree進(jìn)行兩種查詢運算,一種是對于主鍵的范圍查找和分頁查找,另一種是從根節(jié)點開始,進(jìn)行隨機查找。
三、mysql的存儲引擎
3.1 mysql innoDB (葉子節(jié)點直接放置數(shù)據(jù))
3.1 mysql innoDB (葉子節(jié)點直接放置數(shù)據(jù))
存放的是對應(yīng)的行記錄
1、InnoDB是通過B+Tree結(jié)構(gòu)對主鍵創(chuàng)建索引,然后葉子節(jié)點中存儲記錄,如果沒有主鍵,那么會選擇唯一鍵,如果沒有唯一鍵,那么會生成一個6位的row_id來作為主鍵
2、如果創(chuàng)建索引的鍵是其他字段,那么在葉子節(jié)點中存儲的是該記錄的主鍵,然后在通過主鍵索引找到對應(yīng)的記錄
在name上建立索引
在name列上存放的是ID,然后通過ID去找到對應(yīng)的key和數(shù)據(jù)
3.1 mysql MyISAM
下面0X0022其實就是地址,顯示根據(jù)我們的ID,找到我們的地址,然后通過地址去找到對應(yīng)的表對應(yīng)的數(shù)據(jù)
四、索引的分類
mysql索引的五種類型:主鍵索引、唯一索引、普通索引和全文索引、組合索引。通過給字段添加索引可以提高數(shù)據(jù)的讀取速度,提高項目的并發(fā)能力和抗壓能力
- 主鍵索引:> 主鍵是一種唯一性索引,但它必須指定為PRIMARY KEY,每個表只能有一個主鍵
 - 唯一索引 > 索引列的所有值都只能出現(xiàn)一次,即必須唯一,值可以為空
 - 普通索引 > 基本的索引類型,值可以為空,沒有唯一性的限制
 - 全文索引 > 全文索引的索引類型為FULLTEXT,全文索引可以在 varchar、char、text類型的列上創(chuàng)建
 - 組合索引 > 多列值組成的一個索引,專門用于組合搜索
 
五、mysql的存儲引擎
小結(jié)
寫這篇文章的時候,小農(nóng)的公司群消息不斷,因為項目中有問題需要我去解決,今天的mysql索引機制就到這里了.


























 
 
 





 
 
 
 