儘管樹的走訪效率極高,索引查找仍然有「不如預期快」的情況。這個矛盾長年助長了「索引退化(degenerated index)」的迷思,該迷思主張重建索引是萬靈丹。然而瑣碎的敘述即使用了索引也可能很慢,真正的原因其實可以用前面兩節的內容解釋清楚。

拖慢索引查找的兩個成分#

成分一:葉節點鏈

回到圖 1.3 中搜尋「57」的例子。索引裡顯然有兩筆符合的條目——更精確地說是「至少兩筆」:下一個葉節點可能還有更多 57。資料庫必須讀取下一個葉節點才能確認是否還有匹配。也就是說,索引查找不只要做樹走訪,還得沿著葉節點鏈往下走

成分二:存取資料表

即使只是單一葉節點,也可能包含大量命中——常常是數百筆。而對應的資料表資料通常散落在許多資料表區塊中,因此每一筆命中都要多一次資料表存取。

一次索引查找需要三個步驟:

  1. 樹的走訪(tree traversal)
  2. 沿著葉節點鏈前進
  3. 抓取資料表資料

只有第一步的存取區塊數有上界(即索引深度)。另外兩步可能需要存取大量區塊——它們才是慢索引查找的元凶。

三種索引操作#

「慢索引」迷思的源頭,是誤以為索引查找只做樹走訪,於是推論慢索引一定是樹「壞掉」或「不平衡」。事實上,你可以直接問資料庫它如何使用索引。Oracle 在這方面相當詳盡,用三種不同的操作描述基本的索引查找:

  • INDEX UNIQUE SCAN:只執行樹走訪。當唯一性約束(unique constraint)保證搜尋條件最多只會匹配一筆時,Oracle 會採用此操作。
  • INDEX RANGE SCAN:執行樹走訪並沿葉節點鏈尋找所有匹配條目。這是「可能匹配多筆」時的後備操作。
  • TABLE ACCESS BY INDEX ROWID:從資料表取回該列。此操作(通常)會為前面索引掃描匹配到的每一筆記錄執行一次。

關鍵在於:INDEX RANGE SCAN 有可能讀取索引的一大部分。若再為每一列多做一次資料表存取,即使用了索引,查詢依然會很慢