面試題:MySQL性能調優——索引詳解與索引的優化 沒用


——索引優化,可以說是數據庫相關優化、理解尤其是查詢優化中最常用的優化手段之一。所以,只有深入索引的實現原理、存儲方式、不同索引間區別,才能設計或使用最優的索引,最大幅度的提升查詢效率!

 

一、BTree索引

1、概述

Mysql數據庫中使用最頻繁的索引類型,基本所有存儲引擎都支持BTree索引。正是其優異的檢索表現,才使其有這樣的地位。

2、存儲結構

正如其名,這類索引的物理文件大多就是以BTree結構來存儲的,但會有不同的存儲引擎在使用BTree索引時,對存儲結構稍作修改,比如MyISAM存儲引擎,使用B+Tree的數據結構,它相對與BTree結構,所有的數據都存放在葉子節點上,且把葉子節點通過指針連接到一起,形成了一條數據鏈表,以加快相鄰數據的檢索效率。

(對B樹、B+樹不熟悉的,可參看博客索引基礎——B-Tree、B+Tree、紅黑樹、B*Tree數據結構 或《算法導論》)

 

另外,對於innoDB存儲引擎,雖然同樣使用B+Tree作為索引的存儲結構,但具體實現卻與MyISAM截然不同,這也是作為MyISAM與InnoDB存儲引擎的一個重要區別反復被面試官問到。(關於MyISAM與InnoDB存儲引擎可參看博客MySQL架構設計——MyISAM存儲引擎與InnoDB存儲引擎

 

(1)MyISAM引擎索引結構的葉子節點的數據域,存放的並不是實際的數據記錄,而是數據記錄的地址。索引文件與數據文件分離,這樣的索引稱為“非聚簇索引”。MyISAM的主索引與輔助索引區別並不大,只是主鍵索引不能有重復的關鍵字。

如下圖所示為非聚簇索引的主鍵索引:

其檢索算法:先按照B+Tree的檢索算法檢索,找到指定關鍵字,則取出對應數據域的值,作為地址取出數據記錄。

(2)InnoDB引擎索引結構的葉子節點的數據域,存放的就是實際的數據記錄(對於主索引,此處會存放表中所有的數據記錄;對於輔助索引此處會引用主鍵,檢索的時候通過主鍵到主鍵索引中找到對應數據行),或者說,InnoDB的數據文件本身就是主鍵索引文件,這樣的索引被稱為“聚簇索引”,一個表只能有一個聚簇索引。

如下圖所示為聚簇索引的主鍵索引:

 

二、Hash索引

1、概述及存儲結構

主要就是通過Hash算法(常見的Hash算法有直接定址法、平方取中法、折疊法、除數取余法、隨機數法),將數據庫字段數據轉換成定長的Hash值,與這條數據的行指針一並存入Hash表的對應位置;如果發生Hash碰撞(兩個不同關鍵字的Hash值相同),則在對應Hash鍵下以鏈表形式存儲。

檢索算法:在檢索查詢時,就再次對待查關鍵字再次執行相同的Hash算法,得到Hash值,到對應Hash表對應位置取出數據即可,如果發生Hash碰撞,則需要在取值時進行篩選。目前使用Hash索引的數據庫並不多,主要有Memory等。

2、Hash索引的弊端

一般來說,索引的檢索效率非常高,可以一次定位,不像B-Tree索引需要進行從根節點到葉節點的多次IO操作。有利必有弊,Hash算法在索引的應用也有很多弊端。

a、Hash索引僅僅能滿足等值的查詢,范圍查詢不保證結果正確。因為數據在經過Hash算法后,其大小關系就可能發生變化。

b、Hash索引不能被排序。同樣是因為數據經過Hash算法后,大小關系就可能發生變化,排序是沒有意義的。

c、Hash索引不能避免表數據的掃描。因為發生Hash碰撞時,僅僅比較Hash值是不夠的,需要比較實際的值以判定是否符合要求。

d、Hash索引在發生大量Hash值相同的情況時性能不一定比B-Tree索引高。因為碰撞情況會導致多次的表數據的掃描,造成整體性能的低下,可以通過采用合適的Hash算法一定程度解決這個問題。

e、Hash索引不能使用部分索引鍵查詢。因為當使用組合索引情況時,是把多個數據庫列數據合並后再計算Hash值,所以對單獨列數據計算Hash值是沒有意義的。

 

三、Full-Text索引

1、概述

全文索引,目前MySQL中只有MyISAM存儲引擎支持,並且只有CHAR、VARCHAR、TEXT類型支持。它用於替代效率較低的LIKE模糊匹配操作,而且可以通過多字段組合的全文索引一次性全模糊匹配多個字段。

2、存儲結構

同樣使用B-Tree存放索引數據,但使用的是特定的算法,將字段數據分割后再進行索引(一般每4個字節一次分割),索引文件存儲的是分割前的索引字符串集合,與分割后的索引信息,對應Btree結構的節點存儲的是分割后的詞信息以及它在分割前的索引字符串集合中的位置。

 

四、索引利弊

1、索引的好處

a、提高數據檢索的效率,降低檢索過程中必須要讀取得數據量,降低數據庫IO成本

b、降低數據庫的排序成本。因為索引就是對字段數據進行排序后存儲的,如果待排序的字段與索引鍵字段一致,就在取出數據后不用再次排序了,因為通過索引取得的數據已滿足排序要求。另外,分組操作是先排序后分組,所以索引同樣可以省略分組的排序操作,降低內存與CPU資源的消耗。

2、索引的弊端

a、索引會增加 增、刪、改操作所帶來的IO量與調整索引的計算量

b、索引要占用空間,隨着數據量的不斷增大,索引還會帶來存儲空間的消耗

 

五、判斷是否應該建索引的條件

1、較頻繁的作為查詢條件的字段應該創建索引

2、唯一性太差的字段不適合單獨創建索引,即使頻繁作為查詢條件

3、增、刪、改操作較多的數據庫字段不適合建索引

 

附:計算機內存存取、磁盤存取原理:(轉載自:張洋的博客園http://www.uml.org.cn/sjjm/201107145.asp)

 

主存存取原理

 

目前計算機使用的主存基本都是隨機讀寫存儲器(RAM),現代RAM的結構和存取原理比較復雜,這里本文拋卻具體差別,抽象出一個十分簡單的存取模型來說明RAM的工作原理。

image

圖1

從抽象角度看,主存是一系列的存儲單元組成的矩陣,每個存儲單元存儲固定大小的數據。每個存儲單元有唯一的地址,現代主存的編址規則比較復雜,這里將其簡化成一個二維地址:通過一個行地址和一個列地址可以唯一定位到一個存儲單元。圖5展示了一個4 x 4的主存模型。

主存的存取過程如下:

當系統需要讀取主存時,則將地址信號放到地址總線上傳給主存,主存讀到地址信號后,解析信號並定位到指定存儲單元,然后將此存儲單元數據放到數據總線上,供其它部件讀取。

寫主存的過程類似,系統將要寫入單元地址和數據分別放在地址總線和數據總線上,主存讀取兩個總線的內容,做相應的寫操作。

這里可以看出,主存存取的時間僅與存取次數呈線性關系,因為不存在機械操作,兩次存取的數據的“距離”不會對時間有任何影響,例如,先取A0再取A1和先取A0再取D3的時間消耗是一樣的。

磁盤存取原理

上文說過,索引一般以文件形式存儲在磁盤上,索引檢索需要磁盤I/O操作。與主存不同,磁盤I/O存在機械運動耗費,因此磁盤I/O的時間消耗是巨大的。

圖6是磁盤的整體結構示意圖。

image

圖2

一個磁盤由大小相同且同軸的圓形盤片組成,磁盤可以轉動(各個磁盤必須同步轉動)。在磁盤的一側有磁頭支架,磁頭支架固定了一組磁頭,每個磁頭負責存取一個磁盤的內容。磁頭不能轉動,但是可以沿磁盤半徑方向運動(實際是斜切向運動),每個磁頭同一時刻也必須是同軸的,即從正上方向下看,所有磁頭任何時候都是重疊的(不過目前已經有多磁頭獨立技術,可不受此限制)。

圖7是磁盤結構的示意圖。

image

圖3

盤片被划分成一系列同心環,圓心是盤片中心,每個同心環叫做一個磁道,所有半徑相同的磁道組成一個柱面。磁道被沿半徑線划分成一個個小的段,每個段叫做一個扇區,每個扇區是磁盤的最小存儲單元。為了簡單起見,我們下面假設磁盤只有一個盤片和一個磁頭。

當需要從磁盤讀取數據時,系統會將數據邏輯地址傳給磁盤,磁盤的控制電路按照尋址邏輯將邏輯地址翻譯成物理地址,即確定要讀的數據在哪個磁道,哪個扇區。為了讀取這個扇區的數據,需要將磁頭放到這個扇區上方,為了實現這一點,磁頭需要移動對准相應磁道,這個過程叫做尋道,所耗費時間叫做尋道時間,然后磁盤旋轉將目標扇區旋轉到磁頭下,這個過程耗費的時間叫做旋轉時間。

局部性原理與磁盤預讀

由於存儲介質的特性,磁盤本身存取就比主存慢很多,再加上機械運動耗費,磁盤的存取速度往往是主存的幾百分分之一,因此為了提高效率,要盡量減少磁盤I/O。為了達到這個目的,磁盤往往不是嚴格按需讀取,而是每次都會預讀,即使只需要一個字節,磁盤也會從這個位置開始,順序向后讀取一定長度的數據放入內存。這樣做的理論依據是計算機科學中著名的局部性原理:

當一個數據被用到時,其附近的數據也通常會馬上被使用。

程序運行期間所需要的數據通常比較集中。

由於磁盤順序讀取的效率很高(不需要尋道時間,只需很少的旋轉時間),因此對於具有局部性的程序來說,預讀可以提高I/O效率。

預讀的長度一般為頁(page)的整倍數。頁是計算機管理存儲器的邏輯塊,硬件及操作系統往往將主存和磁盤存儲區分割為連續的大小相等的塊,每個存儲塊稱為一頁(在許多操作系統中,頁得大小通常為4k),主存和磁盤以頁為單位交換數據。當程序要讀取的數據不在主存中時,會觸發一個缺頁異常,此時系統會向磁盤發出讀盤信號,磁盤會找到數據的起始位置並向后連續讀取一頁或幾頁載入內存中,然后異常返回,程序繼續運行。

 

B-/+Tree索引的性能分析

 

到這里終於可以分析B-/+Tree索引的性能了。

上文說過一般使用磁盤I/O次數評價索引結構的優劣。先從B-Tree分析,根據B-Tree的定義,可知檢索一次最多需要訪問h個節點。數據庫系統的設計者巧妙利用了磁盤預讀原理,將一個節點的大小設為等於一個頁,這樣每個節點只需要一次I/O就可以完全載入。為了達到這個目的,在實際實現B-Tree還需要使用如下技巧:

每次新建節點時,直接申請一個頁的空間,這樣就保證一個節點物理上也存儲在一個頁里,加之計算機存儲分配都是按頁對齊的,就實現了一個node只需一次I/O。

B-Tree中一次檢索最多需要h-1次I/O(根節點常駐內存),漸進復雜度為O(h)=O(logdN)。一般實際應用中,出度d是非常大的數字,通常超過100,因此h非常小(通常不超過3)。

綜上所述,用B-Tree作為索引結構效率是非常高的。

而紅黑樹這種結構,h明顯要深的多。由於邏輯上很近的節點(父子)物理上可能很遠,無法利用局部性,所以紅黑樹的I/O漸進復雜度也為O(h),效率明顯比B-Tree差很多。

上文還說過,B+Tree更適合外存索引,原因和內節點出度d有關。從上面分析可以看到,d越大索引的性能越好,而出度的上限取決於節點內key和data的大小:

dmax = floor(pagesize / (keysize + datasize + pointsize)) (pagesize – dmax >= pointsize)

dmax = floor(pagesize / (keysize + datasize + pointsize)) - 1 (pagesize – dmax < pointsize)

floor表示向下取整。由於B+Tree內節點去掉了data域,因此可以擁有更大的出度,擁有更好的性能。

這一章從理論角度討論了與索引相關的數據結構與算法問題,下一章將討論B+Tree是如何具體實現為MySQL中索引,同時將結合MyISAM和InnDB存儲引擎介紹非聚集索引和聚集索引兩種不同的索引實現形式。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM