資料庫索引設計與優化讀書筆記--《四》為SELECT語句建立理想的索引

FreeeLinux發表於2017-07-04

磁碟及CPU時間的基礎假設

I/O時間:

隨機讀 10ms(4KB或8KB的頁)
順序讀 40MB/s

順序掃描的CPU時間:6XZX

檢查一行記錄 5us
FETCH 100us

三星索引

星級是如何給定的

如果與一個查詢相關的索引行是相鄰的,或者至少相距足夠靠近的話,那這個索引就可以被標記上第一顆星。這最小化了必須掃描的索引片的寬度。
如果索引行的順序與查詢 語句的需求一致,則索引可以被標記上第二顆星。這排除了排序操作。
如果索引行包含的查詢語句中的所有列,那麼索引就可以被標記上第三顆星。這避免了訪問表的操作:僅訪問索引就可以了。
對於這三顆星,第三顆通常是最重要的。將一個列排除在索引之外可能會導致許多速度較慢的磁碟隨機讀。我們把一個至少包含第三顆星的索引成為對應查詢語句的寬索引

寬索引:寬索引是指一個至少滿足第三顆星的索引。該索引包含了SELECT語句所涉及的所有列,因而能夠使得查詢只需訪問索引而無需訪問表。

如何構造一個三星索引

為了滿足第一顆星

首先取出所有等值謂詞的列(WHERE COL=…)。把這些列作為索引最開頭的列——以任意順序都可以。這樣,必須掃描的索引片的寬度將被縮減至最窄。

為了滿足第二顆星

將ORDER BY列加入到索引中。不要改變這些列的順序,但是忽略哪些在第一步中已經加入索引的列。由於將ORDER BY語句中的列現在加入了索引,所以結果集中的記錄無需排序就已經是以正確的順序排列了。

為了滿足第三顆星

將查詢語句中剩餘的列加入到索引中去(SELECT LNAME, CITY…),列在索引中新增的順序對查詢語句的效能沒有影響,但是將易變的列放過在最後能夠降低更新的成本,現在,索引已經包含了滿足無需回表的訪問路徑所需的所有列。

我們理想的索引有幾顆星呢?首先它必須得有第三顆星。有時候,第一顆星和第二顆星我們只能二選一。比如存在範圍謂詞時,我們(也許)不得不犧牲第二顆星來滿足一個更窄的索引片(第一顆星):

  • 避免排序——擁有第二顆星
  • 擁有可能的最窄索引片,不僅將需要處理的索引行數降至最低,而且將後續處理量,特別是表中資料行的同步讀,減少到最小——擁有第一顆星。

(一般情況下第一顆星比第二顆重要,但不總是這樣)。

根據星級建立最佳索引的演算法

候選A:(去掉第二顆星)

  1. 取出對於優化器來說不過分複雜的等值謂詞列。將這些列作為索引的前導列——以任意順序皆可。
  2. 將選擇性最好的範圍謂詞作為索引的下一個列,如果存在的話,最好的選擇性是指對於最差的輸入值有最低的過濾因子。只考慮對於優化器來說不過分複雜的範圍謂詞即可。
  3. 以正確的順序新增ORDER BY列(如果ORDER 列有DESC的話,加上DESC)。忽略在第1布或第2步中已經新增的列。
  4. 以任意順序將SELECT語句中的其餘的列新增至索引中(但是需要以不易變的列開始)。

候選B:(去掉第一顆星)
如果候選A引起了所給查詢語句的一次排序操作,那麼還可以設計候選B,對於候選B來說第二顆星比第一顆星更重要。

  1. 取出對於優化器來說不過分複雜的等值謂詞列。將這些列作為索引的前導列——以任意順序皆可。
  2. 以正確的順序新增ORDER BY列(如果ORDER BY列中有DESC的話,加上DESC)。忽略在第一步中已經新增的列。

由於現在的硬體條件下排序速度很快,所以如果一個程式取出結果集的所有行,那麼候選A可能和候選B一樣快,甚至比候選B更快。然而,如果一個程式只需獲取能夠填充滿一個螢幕的資料量,那麼候選B可能比候選A快很多。如果訪問路徑中沒有排序的話,資料庫管理系統只要一次一次地讀取資料行就能對結果集進行物化。如果結果集很大的話,為了產生第一屏的資料,候選A需要排序可能會花費非常長的時間

(我的理解是:在LIMIT和OFFSET這類SQL中尤為明顯,從百萬行級的結果集中每次按順序取20行,那麼如果沒有第二顆星,每次事務都需要排序的話,那麼效率是極差的)。

設計索引的要求

機械性的為每一個查詢設計最佳索引也是不明智的,因為索引的維護可能會使得一些程式速度太慢或者使磁碟負載超負荷。實際情況中更常見的情況是,只對那些由於不合適的索引而導致速度太慢的查詢語句進行索引設計。

相關文章