儘管樹的走訪效率極高,索引查找仍然有「不如預期快」的情況。這個矛盾長年助長了「索引退化(degenerated index)」的迷思,該迷思主張重建索引是萬靈丹。然而瑣碎的敘述即使用了索引也可能很慢,真正的原因其實可以用前面兩節的內容解釋清楚。
拖慢索引查找的兩個成分#
成分一:葉節點鏈
回到圖 1.3 中搜尋「57」的例子。索引裡顯然有兩筆符合的條目——更精確地說是「至少兩筆」:下一個葉節點可能還有更多 57。資料庫必須讀取下一個葉節點才能確認是否還有匹配。也就是說,索引查找不只要做樹走訪,還得沿著葉節點鏈往下走。
成分二:存取資料表
即使只是單一葉節點,也可能包含大量命中——常常是數百筆。而對應的資料表資料通常散落在許多資料表區塊中,因此每一筆命中都要多一次資料表存取。
一次索引查找需要三個步驟:
- 樹的走訪(tree traversal)
- 沿著葉節點鏈前進
- 抓取資料表資料
只有第一步的存取區塊數有上界(即索引深度)。另外兩步可能需要存取大量區塊——它們才是慢索引查找的元凶。
三種索引操作#
「慢索引」迷思的源頭,是誤以為索引查找只做樹走訪,於是推論慢索引一定是樹「壞掉」或「不平衡」。事實上,你可以直接問資料庫它如何使用索引。Oracle 在這方面相當詳盡,用三種不同的操作描述基本的索引查找:
INDEX UNIQUE SCAN:只執行樹走訪。當唯一性約束(unique constraint)保證搜尋條件最多只會匹配一筆時,Oracle 會採用此操作。INDEX RANGE SCAN:執行樹走訪並沿葉節點鏈尋找所有匹配條目。這是「可能匹配多筆」時的後備操作。TABLE ACCESS BY INDEX ROWID:從資料表取回該列。此操作(通常)會為前面索引掃描匹配到的每一筆記錄執行一次。
關鍵在於:
INDEX RANGE SCAN有可能讀取索引的一大部分。若再為每一列多做一次資料表存取,即使用了索引,查詢依然會很慢。