數據庫索引原理內附大量面試題

數據庫學習筆記(一)—— 數據庫索引全解,該文章是裝載!!!!!!!文章,只是對以下文章進行了總結,主體內容來自下面幾個部分:

  1. CSDN藍色楓魂博主的(裝載)《數據庫索引全解(What is Database Index)》,CSDN博主輝仔的《數據庫索引的實現原理》,CSDN博主專注Java面試整理的《數據庫索引相關面試題》,CSDN博主waeceo的《MySql索引實現原理
  2. 知乎專欄《平衡二叉樹、B樹、B+樹、B*樹 理解其中一種你就都明白了
  3. b站視頻《徹底理解B樹和B+樹!爲何它們常用在數據庫中?
  4. 數據庫系統概念(原書第6版)P268-305

數據庫索引原理內附大量面試題


1. 數據庫索引究竟是什麼

說白了,數據庫的索引就是一個查找問題

許多查詢只涉及到文件中的很少一部分。爲了減少查找這些記錄的開銷,我們可以爲數據庫文件創建索引。數據庫索引,是數據庫管理系統中一個排序的數據結構,以協助快速查詢、更新數據庫表中數據。索引的實現通常使用B樹及其變種B+樹。

在數據之外,數據庫系統還維護着滿足特定查找算法的數據結構,這些數據結構以某種方式引用(指向)數據,這樣就可以在這些數據結構上實現高級查找算法。這種數據結構,就是索引

爲表設置索引要付出代價的:一是增加了數據庫的存儲空間,二是在插入和修改數據時要花費較多的時間(因爲索引也要隨之變動)。

在這裏插入圖片描述
上圖展示了一種可能的索引方式。左邊是數據表,一共有兩列七條記錄,最左邊的是數據記錄的物理地址(注意邏輯上相鄰的記錄在磁盤上也並不是一定物理相鄰的)。爲了加快Col2的查找,可以維護一個右邊所示的二叉查找樹,每個節點分別包含索引鍵值和一個指向對應數據記錄物理地址的指針,這樣就可以運用二叉查找在O(log2n)的複雜度內獲取到相應數據。

創建索引可以大大提高系統的性能:

  1. 通過創建唯一性索引,可以保證數據庫表中每一行數據的唯一性。
  2. 可以大大加快數據的檢索速度,這也是創建索引的最主要的原因。
  3. 可以加速表和表之間的連接,特別是在實現數據的參考完整性方面特別有意義。
  4. 在使用分組和排序子句進行數據檢索時,同樣可以顯著減少查詢中分組和排序的時間。
  5. 通過使用索引,可以在查詢的過程中,使用優化隱藏器,提高系統的性能。

評論索引技術好快的標準:

  1. 訪問類型(access type):能有效支持的訪問類型。訪問類型可以包括找到具有特定屬性值的記錄,以及找到屬性值落在某個特定範圍內的記錄。
  2. 訪問時間(access time):在查詢中使用該技術找到一個特定數據項或數據項集所需要的時間
  3. 插入時間(insertion time):插入一個數據項所需要的時間。該值包括找到插入這個新數據項的正確位置所需要的時間,以及更新索引結構所需的時間
  4. 刪除時間(deletion time):刪除一個數據項所需要的時間,該值包括找到待刪除元素所需的時間,以及更新索引結構所需的時間
  5. .空間開銷(space overhead):索引結構所佔用的額外空間。倘如存儲索引結構的額外空間大小適度,通常犧牲一定的空間代價來換取性能的提高是值得的

也許會有人要問:增加索引有如此多的優點,爲什麼不對錶中的每一個列創建一個索引呢?因爲,增加索引也有許多不利的方面。

  1. 創建索引和維護索引要耗費時間,這種時間隨着數據量的增加而增加。
  2. 索引需要佔物理空間,除了數據表佔數據空間之外,每一個索引還要佔一定的物理空間,如果要建立聚簇索引,那麼需要的空間就會更大。
  3. 當對錶中的數據進行增加、刪除和修改的時候,索引也要動態的維護,這樣就降低了數據的維護速度

索引是建立在數據庫表中的某些列的上面。在創建索引的時候,應該考慮在哪些列上可以創建索引,在哪些列上不能創建索引。一般來說,應該在這些列上創建索引:在經常需要搜索的列上,可以加快搜索的速度;在作爲主鍵的列上,強制該列的唯一性和組織表中數據的排列結構;在經常用在連接的列上,這些列主要是一些外鍵,可以加快連接的速度;在經常需要根據範圍進行搜索的列上創建索引,因爲索引已經排序,其指定的範圍是連續的;在經常需要排序的列上創建索引,因爲索引已經排序,這樣查詢可以利用索引的排序,加快排序查詢時間;在經常使用在WHERE子句中的列上面創建索引,加快條件的判斷速度

同樣,對於有些列不應該創建索引。一般來說,不應該創建索引的的這些列具有下列特點:

  1. **對於那些在查詢中很少使用或者參考的列不應該創建索引。**這是因爲,既然這些列很少使用到,因此有索引或者無索引,並不能提高查詢速度。相反,由於增加了索引,反而降低了系統的維護速度和增大了空間需求。
  2. **對於那些只有很少數據值的列也不應該增加索引。**這是因爲,由於這些列的取值很少,例如人事表的性別列,在查詢的結果中,結果集的數據行佔了表中數據行的很大比例,即需要在表中搜索的數據行的比例很大。增加索引,並不能明顯加快檢索速度。
  3. 對於那些定義爲text, image和bit數據類型的列不應該增加索引。這是因爲,這些列的數據量要麼相當大要麼取值很少
  4. **當修改性能遠遠大於檢索性能時,不應該創建索引。**這是因爲,修改性能和檢索性能是互相矛盾的。當增加索引時,會提高檢索性能,但是會降低修改性能。當減少索引時,會提高修改性能,降低檢索性能。因此,當修改性能遠遠大於檢索性能時,不應該創建索引。

2. 三種索引:唯一索引、主鍵索引和聚集索引。

根據數據庫的功能,可以在數據庫設計器中創建三種索引:唯一索引、主鍵索引和聚集索引。

2.1 唯一索引

**唯一索引是不允許其中任何兩行具有相同索引值的索引。**當現有數據中存在重複的鍵值時,大多數數據庫不允許將新創建的唯一索引與表一起保存。數據庫還可能防止添加將在表中創建重複鍵值的新數據。例如,如果在employee表中職員的姓(lname)上創建了唯一索引,則任何兩個員工都不能同姓。

2.2 主鍵索引

數據庫表經常有一列或列組合,其值唯一標識表中的每一行。該列稱爲表的主鍵。
在數據庫關係圖中爲表定義主鍵將自動創建主鍵索引,主鍵索引是唯一索引的特定類型。該索引要求主鍵中的每個值都唯一。當在查詢中使用主鍵索引時,它還允許對數據的快速訪問。

2.3 聚集索引

在聚集索引中,表中行的物理順序與鍵值的邏輯(索引)順序相同。一個表只能包含一個聚集索引。
如果某索引不是聚集索引,則表中行的物理順序與鍵值的邏輯順序不匹配。與非聚集索引相比,聚集索引通常提供更快的數據訪問速度。

3. 磁盤

索引一般以文件形式存儲在磁盤上,索引檢索需要磁盤I/O操作。

3.1 磁盤的存儲原理

與主存不同,磁盤I/O存在機械運動耗費,因此磁盤I/O的時間消耗是巨大的。磁盤的整體結構
在這裏插入圖片描述
一個磁盤由大小相同同軸的圓形盤片組成,磁盤可以轉動(各個磁盤必須同步轉動)。在磁盤的一側有磁頭支架,磁頭支架固定了一組磁頭,每個磁頭負責存取一個磁盤的內容。磁頭不能轉動,但是可以沿磁盤半徑方向運動(實際是斜切向運動),每個磁頭同一時刻也必須是同軸的,即從正上方向下看,所有磁頭任何時候都是重疊的(不過目前已經有多磁頭獨立技術,可不受此限制)。

磁盤結構的示意圖:
在這裏插入圖片描述
盤片被劃分成一系列同心環,圓心是盤片中心,每個同心環叫做一個磁道,所有半徑相同的磁道組成一個柱面。磁道被沿半徑線劃分成一個個小的段,每個段叫做一個扇區,每個扇區是磁盤的最小存儲單元。爲了簡單起見,我們下面假設磁盤只有一個盤片和一個磁頭。

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

3. 2 局部性原理與磁盤預讀

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

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

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

4. B-/+ Tree樹的介紹

我在網上找打一個比較nice的視頻,可以去看一下,這裏我就不打算列出來了。

b站視頻《徹底理解B樹和B+樹!爲何它們常用在數據庫中?

4.1 B樹

B樹中每個節點包含了鍵值和鍵值對於的數據對象存放地址指針,所以成功搜索一個對象可以不用到達樹的葉節點。

成功搜索包括節點內搜索和沿某一路徑的搜索,成功搜索時間取決於關鍵碼所在的層次以及節點內關鍵碼的數量

在B樹中查找給定關鍵字的方法是:首先把根結點取來,在根結點所包含的關鍵字K1,…,kj查找給定的關鍵字(可用順序查找或二分查找法),若找到等於給定值的關鍵字,則查找成功;否則,一定可以確定要查的關鍵字在某個Ki或Ki+1之間,於是取Pi所指的下一層索引節點塊繼續查找,直到找到,或指針Pi爲空時查找失敗。

4.2 B+樹

B+樹非葉節點中存放的關鍵碼並不指示數據對象的地址指針非葉節點只是索引部分。所有的葉節點在同一層上,包含了全部關鍵碼和相應數據對象的存放地址指針,且葉節點按關鍵碼從小到大順序鏈接。如果實際數據對象按加入的順序存儲而不是按關鍵碼次數存儲的話,葉節點的索引必須是稠密索引,若實際數據存儲按關鍵碼次序存放的話,葉節點索引時稀疏索引。

B+樹有2個頭指針,一個是樹的根節點,一個是最小關鍵碼的葉節點

所以 B+樹有兩種搜索方法:

一種是按葉節點自己拉起的鏈表順序搜索

一種是從根節點開始搜索,和B樹類似,不過如果非葉節點的關鍵碼等於給定值,搜索並不停止,而是繼續沿右指針,一直查到葉節點上的關鍵碼。所以無論搜索是否成功,都將走完樹的所有層。

B+ 樹中,數據對象的插入和刪除僅在葉節點上進行。

4.3 這兩種處理索引的數據結構的不同之處

  1. B樹中同一鍵值不會出現多次,並且它有可能出現在葉結點,也有可能出現在非葉結點中。而B+樹的鍵一定會出現在葉結點中,並且有可能在非葉結點中也有可能重複出現,以維持B+樹的平衡
  2. 因爲B樹鍵位置不定,且在整個樹結構中只出現一次,雖然可以節省存儲空間,但使得在插入、刪除操作複雜度明顯增加。B+樹相比來說是一種較好的折中
  3. B樹的查詢效率與鍵在樹中的位置有關,最大時間複雜度與B+樹相同(在葉結點的時候),最小時間複雜度爲1(在根結點的時候)。而B+樹的時候複雜度對某建成的樹是固定的

4.4 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)。

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

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

5. MySQL索引實現

在MySQL中,索引屬於存儲引擎級別的概念不同存儲引擎對索引的實現方式是不同的,本文主要討論MyISAM和InnoDB兩個存儲引擎(MySQL數據庫MyISAM和InnoDB存儲引擎的比較)的索引實現方式。

5.1 MyISAM索引實現

MyISAM引擎使用B+Tree作爲索引結構,葉結點的data域存放的是數據記錄的地址。下面是MyISAM索引的原理圖:
在這裏插入圖片描述
這裏設表一共有三列,假設我們以Col1爲主鍵,則圖8是一個MyISAM表的主索引(Primary key)示意。可以看出MyISAM的索引文件僅僅保存數據記錄的地址。在MyISAM中,主索引和輔助索引(Secondary key)在結構上沒有任何區別,只是主索引要求key是唯一的,而輔助索引的key可以重複。如果我們在Col2上建立一個輔助索引,則此索引的結構如下圖所示:
在這裏插入圖片描述
同樣也是一顆B+Tree,data域保存數據記錄的地址。因此,MyISAM中索引檢索的算法爲首先按照B+Tree搜索算法搜索索引,如果指定的Key存在,則取出其data域的值,然後以data域的值爲地址,讀取相應數據記錄。

MyISAM的索引方式也叫做**“非聚集”的,之所以這麼稱呼是爲了與InnoDB的聚集索引區分。**

5.2 InnoDB索引實現

雖然InnoDB也使用B+Tree作爲索引結構,但具體實現方式卻與MyISAM截然不同

第一個重大區別是InnoDB的數據文件本身就是索引文件。從上文知道,MyISAM索引文件和數據文件是分離的,索引文件僅保存數據記錄的地址。而在InnoDB中,表數據文件本身就是按B+Tree組織的一個索引結構,這棵樹的葉結點data域保存了完整的數據記錄。這個索引的key是數據表的主鍵,因此InnoDB表數據文件本身就是主索引。
在這裏插入圖片描述
圖10是InnoDB主索引(同時也是數據文件)的示意圖,可以看到葉結點包含了完整的數據記錄。這種索引叫做聚集索引。因爲InnoDB的數據文件本身要按主鍵聚集,所以InnoDB要求表必須有主鍵(MyISAM可以沒有),如果沒有顯式指定,則
MySQL系統會自動選擇一個可以唯一標識數據記錄的列作爲主鍵
,如果不存在這種列,則MySQL自動爲InnoDB表生成一個隱含字段作爲主鍵,這個字段長度爲6個字節,類型爲長整形

第二個與MyISAM索引的不同是InnoDB的輔助索引data域存儲相應記錄主鍵的值而不是地址。換句話說,InnoDB的所有輔助索引都引用主鍵作爲data域。例如,圖11爲定義在Col3上的一個輔助索引:
在這裏插入圖片描述
這裏以英文字符的ASCII碼作爲比較準則。聚集索引這種實現方式使得
按主鍵的搜索十分高效
,但是輔助索引搜索需要檢索兩遍索引首先檢索輔助索引獲得主鍵,然後用主鍵到主索引中檢索獲得記錄。

瞭解不同存儲引擎的索引實現方式對於正確使用和優化索引都非常有幫助,例如知道了InnoDB的索引實現後,就很容易明白爲什麼不建議使用過長的字段作爲主鍵,因爲所有輔助索引都引用主索引,過長的主索引會令輔助索引變得過大。再例如,用非單調的字段作爲主鍵在InnoDB中不是個好主意,因爲InnoDB數據文件本身是一顆B+Tree,非單調的主鍵會造成在插入新記錄時數據文件爲了維持B+Tree的特性而頻繁的分裂調整,十分低效,而使用自增字段作爲主鍵則是一個很好的選擇

6. MySQL索引的使用策略以及優化

爲了討論索引策略,需要一個數據量不算小的數據庫作爲示例。本文選用MySQL官方文檔中提供的示例數據庫之一:employees。**這個數據庫關係複雜度適中,且數據量較大。**下圖是這個數據庫的E-R關係圖(引用自MySQL官方手冊):
在這裏插入圖片描述

6.1 最左前綴原理與相關優化

高效使用索引的首要條件是知道什麼樣的查詢會使用到索引,這個問題和B+Tree中的“最左前綴原理”有關,下面通過例子說明最左前綴原理。

這裏先說一下聯合索引的概念。在上文中,我們都是假設索引只引用了單個的列,實際上,MySQL中的索引可以以一定順序引用多個列,這種索引叫做聯合索引,一般的,一個聯合索引是一個有序元組<a1, a2, …, an>,其中各個元素均爲數據表的一列,實際上要嚴格定義索引需要用到關係代數,但是這裏我不想討論太多關係代數的話題,因爲那樣會顯得很枯燥,所以這裏就不再做嚴格定義。另外,單列索引可以看成聯合索引元素數爲1的特例。

以employees.titles表爲例,下面先查看其上都有哪些索引:
在這裏插入圖片描述
從結果中可以到titles表的主索引爲<emp_no, title, from_date>,還有一個輔助索引<emp_no>。爲了避免多個索引使事情變複雜(MySQL的SQL優化器在多索引時行爲比較複雜),這裏我們將輔助索引drop掉
在這裏插入圖片描述
這樣就可以專心分析索引PRIMARY的行爲了。

6.2 全列匹配

在這裏插入圖片描述
很明顯,當按照索引中所有列進行精確匹配(這裏精確匹配指“=”或“IN”匹配)時,索引可以被用到。這裏有一點需要注意,理論上索引對順序是敏感的,但是由於MySQL的查詢優化器會自動調整where子句的條件順序以使用適合的索引,例如我們將where中的條件順序顛倒:
在這裏插入圖片描述
效果是一樣的。

6.3 最左前綴匹配

在這裏插入圖片描述
當查詢條件精確匹配索引的左邊連續一個或幾個列時,如<emp_no>或<emp_no, title>,所以可以被用到,但是隻能用到一部分,即條件所組成的最左前綴。上面的查詢從分析結果看用到了PRIMARY索引,但是key_len爲4,說明只用到了索引的第一列前綴。

6.4 查詢條件用到了索引中列的精確匹配,但是中間某個條件未提供

在這裏插入圖片描述
此時索引使用情況和情況二相同,因爲title未提供,所以查詢只用到了索引的第一列,而後面的from_date雖然也在索引中,但是由於title不存在而無法和左前綴連接,因此需要對結果進行掃描過濾from_date(這裏由於emp_no唯一,所以不存在掃描)。如果想讓from_date也使用索引而不是where過濾,可以增加一個輔助索引<emp_no, from_date>,此時上面的查詢會使用這個索引。除此之外,還可以使用一種稱之爲“隔離列”的優化方法,將emp_no與from_date之間的“坑”填上。

首先我們看下title一共有幾種不同的值:
在這裏插入圖片描述
只有7種。在這種成爲“坑”的列值比較少的情況下,可以考慮用“IN”來填補這個“坑”從而形成最左前綴
在這裏插入圖片描述
這次key_len爲59,說明索引被用全了,但是從type和rows看出IN實際上執行了一個range查詢,這裏檢查了7個key。看下兩種查詢的性能比較:
在這裏插入圖片描述
“填坑”後性能提升了一點。如果經過emp_no篩選後餘下很多數據,則後者性能優勢會更加明顯。當然,如果title的值很多,用填坑就不合適了,必須建立輔助索引

6.5 查詢條件沒有指定索引第一列

在這裏插入圖片描述
由於不是最左前綴,索引這樣的查詢顯然用不到索引。

6.6 匹配某列前綴字符串

在這裏插入圖片描述
此時可以用到索引,但是如果通配符不是隻出現在末尾,則無法使用索引。(原文表述有誤,如果通配符%不出現在開頭,則可以用到索引,但根據具體情況不同可能只會用其中一個前綴)

6.7 範圍查詢

在這裏插入圖片描述
範圍列可以用到索引(必須是最左前綴),但是範圍列後面的列無法用到索引。同時,索引最多用於一個範圍列,因此如果查詢條件中有兩個範圍列則無法全用到索引。
在這裏插入圖片描述
可以看到索引對第二個範圍索引無能爲力。這裏特別要說明MySQL一個有意思的地方,那就是僅用explain可能無法區分範圍索引和多值匹配,因爲在type中這兩者都顯示爲range。同時,用了“between”並不意味着就是範圍查詢,例如下面的查詢:
在這裏插入圖片描述
看起來是用了兩個範圍查詢,但作用於emp_no上的**“BETWEEN”實際上相當於“IN”,也就是說emp_no實際是多值精確匹配。可以看到這個查詢用到了索引全部三個列。因此在MySQL中要謹慎地區分多值匹配和範圍匹配,否則會對MySQL的行爲產生困惑。**

6.8 查詢條件中含有函數或表達式

很不幸,如果查詢條件中含有函數或表達式,則MySQL不會爲這列使用索引(雖然某些在數學意義上可以使用)。例如:
在這裏插入圖片描述
雖然這個查詢和情況五中功能相同,但是由於使用了函數left,則無法爲title列應用索引,而情況五中用LIKE則可以。再如:
在這裏插入圖片描述
顯然這個查詢等價於查詢emp_no爲10001的函數,但是由於查詢條件是一個表達式,MySQL無法爲其使用索引。看來MySQL還沒有智能到自動優化常量表達式的程度,因此在寫查詢語句時儘量避免表達式出現在查詢中,而是先手工私下代數運算,轉換爲無表達式的查詢語句。

6.9 索引選擇性與前綴索引

既然索引可以加快查詢速度,**那麼是不是只要是查詢語句需要,就建上索引?答案是否定的。**因爲索引雖然加快了查詢速度,但索引也是有代價的:索引文件本身要消耗存儲空間,同時索引會加重插入、刪除和修改記錄時的負擔,另外,MySQL在運行時也要消耗資源維護索引,因此索引並不是越多越好。一般兩種情況下不建議建索引

第一種情況是表記錄比較少,例如一兩千條甚至只有幾百條記錄的表,沒必要建索引,讓查詢做全表掃描就好了。至於多少條記錄纔算多,這個個人有個人的看法,我個人的經驗是以2000作爲分界線,記錄數不超過 2000可以考慮不建索引,超過2000條可以酌情考慮索引。

另一種不建議建索引的情況是索引的選擇性較低。所謂索引的選擇性(Selectivity),是指不重複的索引值(也叫基數,Cardinality)與表記錄數(#T)的比值:

Index Selectivity = Cardinality / #T

顯然選擇性的取值範圍爲(0, 1],選擇性越高的索引價值越大,這是由B+Tree的性質決定的。

6.10 性別列適不適合建立索引?

例如,上文用到的employees.titles表,如果title字段經常被單獨查詢,是否需要建索引,我們看一下它的選擇性:
在這裏插入圖片描述
title的選擇性不足0.0001(精確值爲0.00001579),所以實在沒有什麼必要爲其單獨建索引。

有一種與索引選擇性有關的索引優化策略叫做前綴索引,就是用列的前綴代替整個列作爲索引key,當前綴長度合適時,可以做到既使得前綴索引的選擇性接近全列索引,同時因爲索引key變短而減少了索引文件的大小和維護開銷。下面以employees.employees表爲例介紹前綴索引的選擇和使用。

從圖12可以看到employees表只有一個索引<emp_no>,那麼如果我們想按名字搜索一個人,就只能全表掃描了:
在這裏插入圖片描述
如果頻繁按名字搜索員工,這樣顯然效率很低,因此我們可以考慮建索引。有兩種選擇,建<first_name>或<first_name, last_name>,看下兩個索引的選擇性:
在這裏插入圖片描述
<first_name>顯然選擇性太低,<first_name, last_name>選擇性很好,但是first_name和last_name加起來長度爲30,有沒有兼顧長度和選擇性的辦法?可以考慮用first_name和last_name的前幾個字符建立索引,例如<first_name, left(last_name, 3)>,看看其選擇性:
在這裏插入圖片描述
選擇性還不錯,但離0.9313還是有點距離,那麼把last_name前綴加到4:在這裏插入圖片描述
這時選擇性已經很理想了,而這個索引的長度只有18,比<first_name, last_name>短了接近一半,我們把這個前綴索引 建上:
在這裏插入圖片描述
此時再執行一遍按名字查詢,比較分析一下與建索引前的結果:
在這裏插入圖片描述
性能的提升是顯著的,查詢速度提高了120多倍。

前綴索引兼顧索引大小和查詢速度,但是其缺點是不能用於ORDER BY和GROUP BY操作,也不能用於Covering index(即當索引本身包含查詢所需全部數據時,不再訪問數據文件本身)。

6.11 InnoDB的主鍵選擇與優化

在使用InnoDB存儲引擎時,如果沒有特別的需要,請永遠使用一個與業務無關的自增字段作爲主鍵。

經常看到有帖子或博客討論主鍵選擇問題,有人建議使用業務無關的自增主鍵,有人覺得沒有必要,完全可以使用如學號或身份證號這種唯一字段作爲主鍵。不論支持哪種論點,大多數論據都是業務層面的。如果從數據庫索引優化角度看,使用InnoDB引擎而不使用自增主鍵絕對是一個糟糕的主意。

上文討論過InnoDB的索引實現,InnoDB使用聚集索引,數據記錄本身被存於主索引(一顆B+Tree)的葉子節點上。這就要求同一個葉子節點內(大小爲一個內存頁或磁盤頁)的各條數據記錄按主鍵順序存放,因此每當有一條新的記錄插入時,MySQL會根據其主鍵將其插入適當的節點和位置,如果頁面達到裝載因子(InnoDB默認爲15/16),則開闢一個新的頁(節點)。

如果表使用自增主鍵,那麼每次插入新的記錄,記錄就會順序添加到當前索引節點的後續位置,當一頁寫滿,就會自動開闢一個新的頁。如下圖所示:

在這裏插入圖片描述

7. 面試題彙總

7.1 索引的底層實現原理和優化

B+樹,經過優化的B+樹,主要是在所有的葉子結點中增加了指向下一個葉子節點的指針,因此InnoDB建議爲大部分表使用默認自增的主鍵作爲主索引。

7.2 什麼情況下設置了索引但無法使用

  1. 以“%”開頭的LIKE語句,模糊匹配
  2. OR語句前後沒有同時使用索引
  3. 數據類型出現隱式轉化(如varchar不加單引號的話可能會自動轉換爲int型)

7.3 簡單描述mysql中,索引,主鍵,唯一索引,聯合索引的區別,對數據庫的性能有什麼影響(從讀寫兩方面)

索引是一種特殊的文件(InnoDB數據表上的索引是表空間的一個組成部分),它們包含着對數據表裏所有記錄的引用指針。

普通索引(由關鍵字KEY或INDEX定義的索引)的唯一任務是加快對數據的訪問速度。

普通索引允許被索引的數據列包含重複的值。如果能確定某個數據列將只包含彼此各不相同的值,在爲這個數據列創建索引的時候就應該用關鍵字UNIQUE把它定義爲一個唯一索引。也就是說, 唯一索引可以保證數據記錄的唯一性。

主鍵,是一種特殊的唯一索引,在一張表中只能定義一個主鍵索引,主鍵用於唯一標識一條記錄,使用關鍵字PRIMARY KEY 來創建。

索引可以覆蓋多個數據列,如像INDEX(columnA, columnB)索引,這就是聯合索引。

索引可以極大的提高數據的查詢速度,但是會降低插入、刪除、更新表的速度,因爲在執行這些寫操作時,還要操作索引文件。

7.4 對於關係型數據庫而言,索引是相當重要的概念,請回答有關索引的幾個問題:

a)索引的目的是什麼?

快速訪問數據表中的特定信息,提高檢索速度

創建唯一性索引,保證數據庫表中每一行數據的唯一性。

加速表和表之間的連接

使用分組和排序子句進行數據檢索時,可以顯著減少查詢中分組和排序的時間

b)、索引對數據庫系統的負面影響是什麼?

負面影響:

創建索引和維護索引需要耗費時間,這個時間隨着數據量的增加而增加;索引需要佔用物理空間,不光是表需要佔用數據空間,每個索引也需要佔用物理空間;當對錶進行增、刪、改、的時候索引也要動態維護,這樣就降低了數據的維護速度。

c)、爲數據表建立索引的原則有哪些?

在最頻繁使用的、用以縮小查詢範圍的字段上建立索引。

在頻繁使用的、需要排序的字段上建立索引

d)、 什麼情況下不宜建立索引?

對於查詢中很少涉及的列或者重複值比較多的列,不宜建立索引。

對於一些特殊的數據類型,不宜建立索引,比如文本字段(text)等

7.5 你怎麼看到爲表格定義的所有索引?

索引是通過以下方式爲表格定義的:

SHOW INDEX FROM ;

7.6 實現索引的方式? 索引的原理? 索引的代價? 索引的類型?

實現索引的方式有兩種:針對一張表的某些字段創建具體的索引,如對oracle: create index 索引名稱 on 表名(字段名);在創建表時爲字段建立主鍵約束或者唯一約束,系統將自動爲其建立索引。

索引的原理:根據建立索引的字段建立索引表,存放字段值以及對應記錄的物理地址,從而在搜索的時候根據字段值搜索索引表的到物理地址直接訪問記錄。

引入索引雖然提高了查詢速度,但本身佔用一定的系統存儲容量和系統處理時間,需要根據實際情況進行具體的分析.

索引的類型有:B樹索引,位圖索引,函數索引等。

7.7 怎樣創建一個一個索引,索引使用的原則,有什麼優點和缺點

創建標準索引: CREATE INDEX 索引名 ON 表名 (列名) TABLESPACE 表空間名;

創建唯一索引: CREATE unique INDEX 索引名 ON 表名 (列名) TABLESPACE 表空間名;

創建組合索引: CREATE INDEX 索引名 ON 表名 (列名1,列名2) TABLESPACE 表空間名;

創建反向鍵索引: CREATE INDEX 索引名 ON 表名 (列名) reverse TABLESPACE 表空間名;

索引使用原則:

索引字段建議建立NOT NULL約束

經常與其他表進行連接的表,在連接字段上應該建立索引;

經常出現在Where子句中的字段且過濾性很強的,特別是大表的字段,應該建立索引;

可選擇性高的關鍵字 ,應該建立索引;

可選擇性低的關鍵字,但數據的值分佈差異很大時,選擇性數據比較少時仍然可以利用索引提高效率

複合索引的建立需要進行仔細分析;儘量考慮用單字段索引代替:

A、正確選擇複合索引中的第一個字段,一般是選擇性較好的且在where子句中常用的字段上;

B、複合索引的幾個字段經常同時以AND方式出現在Where子句中可以建立複合索引;否則單字段索引;

C、如果複合索引中包含的字段經常單獨出現在Where子句中,則分解爲多個單字段索引;

D、如果複合索引所包含的字段超過3個,那麼仔細考慮其必要性,考慮減少複合的字段;

E、如果既有單字段索引,又有這幾個字段上的複合索引,一般可以刪除複合索引;

頻繁DML的表,不要建立太多的索引;

不要將那些頻繁修改的列作爲索引列;

索引的優缺點:

有點:

  1. 創建唯一性索引,保證數據庫表中每一行數據的唯一性

  2. 大大加快數據的檢索速度,這也是創建索引的最主要的原因

  3. 加速表和表之間的連接,特別是在實現數據的參考完整性方面特別有意義。

  4. 在使用分組和排序子句進行數據檢索時,同樣可以顯著減少查詢中分組和排序的時間。

缺點:

  1. 索引創建在表上,不能創建在視圖上

  2. 創建索引和維護索引要耗費時間,這種時間隨着數據量的增加而增加

  3. 索引需要佔物理空間,除了數據表佔數據空間之外,每一個索引還要佔一定的物理空間,如果要建立聚簇索引,那麼需要的空間就會更大

  4. 當對錶中的數據進行增加、刪除和修改的時候,索引也要動態的維護,降低了數據的維護速度

7.8 索引的實現方式

都是 B+樹索引, Innodb 是索引組織表, myisam 是堆表, 索引組織表和堆表的區別要熟悉

7.9 Oracle索引分爲哪幾類,說出唯一索引和位圖索引的概念。

Oracle索引有B樹索引,位圖索引,函數索引,簇索引等。

唯一索引也是B樹索引的一種,它要求被索引的字段值不可以重複。在創建的時候使用B樹算法創建。

位圖索引並不是採用象唯一索引那樣存儲(索引字段值,記錄ROWID)來創建索引段的,而是爲每一個唯一的字段值創建一個位圖,位圖中使用位元來對應一個記錄的ROWID。位元到ROWID是通過映射的到的。

7.10 如何寫 sql 能夠有效的使用到複合索引。

由於複合索引的組合索引,類似多個木板拼接在一起,如果中間斷了就無法用了,所以要能用到複合索引,首先開頭(第一列)要用上,比如index(a,b) 這種,我們可以select table tname where a=XX 用到第一列索引 如果想用第二列 可以 and b=XX 或者and b like‘TTT%’

7.11 當數據表中A、B字段做了組合索引,那麼單獨使用A或單獨使用B會有索引效果嗎?(使用like查詢如何有索引效果)

看A、B兩字段做組合索引的時候,誰在前面,誰在後面,如果A在前,那麼單獨使用A會有索引效果,單獨使用B則沒有,反之亦然。同理,使用like模糊查詢時,如果只是使用前面%,那麼有索引效果,如果使用雙%號匹配,那麼則無索引效果

7.12 MYsql 的索引原理,索引的類型有哪些,如何創建合理的索引,索引如何優化。

索引是通過複雜的算法,提高數據查詢性能的手段。從磁盤io到內存io的轉變

普通索引,主鍵,唯一,單列/多列索引建索引的幾大原則

1.最左前綴匹配原則,非常重要的原則,mysql會一直向右匹配直到遇到範圍查詢(>、<、between、like)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)順序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引則都可以用到,a,b,d的順序可以任意調整。

2.=和in可以亂序,比如a = 1 and b = 2 and c = 3 建立(a,b,c)索引可以任意順序,mysql的查詢優化器會幫你優化成索引可以識別的形式

3.儘量選擇區分度高的列作爲索引,區分度的公式是count(distinct col)/count(*),表示字段不重複的比例,比例越大我們掃描的記錄數越少,唯一鍵的區分度是1,而一些狀態、性別字段可能在大數據面前區分度就是0,那可能有人會問,這個比例有什麼經驗值嗎?使用場景不同,這個值也很難確定,一般需要join的字段我們都要求是0.1以上,即平均1條掃描10條記錄

4.索引列不能參與計算,保持列“乾淨”,比如from_unixtime(create_time) = ’2014-05-29’就不能使用到索引,原因很簡單,b+樹中存的都是數據表中的字段值,但進行檢索時,需要把所有元素都應用函數才能比較,顯然成本太大。所以語句應該寫成create_time = unix_timestamp(’2014-05-29’);

5.儘量的擴展索引,不要新建索引。比如表中已經有a的索引,現在要加(a,b)的索引,那麼只需要修改原來的索引即可

7.13 聚集索引和非聚集索引的區別。

“聚簇”就是索引和記錄緊密在一起。

非聚簇索引 索引文件和數據文件分開存放,索引文件的葉子頁只保存了主鍵值,要定位記錄還要去查找相應的數據塊。

7.14 數據庫中 BTREE 和 B+tree 區別。

B+是btree的變種,本質都是btree,btree+與B-Tree相比,B+Tree有以下不同點:

每個節點的指針上限爲2d而不是2d+1。

內節點不存儲data,只存儲key;葉子節點不存儲指針。

Btree 怎麼分裂的,什麼時候分裂,爲什麼是平衡的。

Key 超過1024才分裂B樹爲甚會分裂? 因爲隨着數據的增多,一個結點的key滿了,爲了保持B樹的特性,就會產生分裂,就向紅黑樹和AVL樹爲了保持樹的性質需要進行旋轉一樣!

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