B-tree索引類型,實現為“btree”訪問方法,適用於可以排序的數據。換句話說,必須為數據類型定義“更大”、“更大或相等”、“更小”、“更小或相等”和“相等”操作符。
在B-tree的數據結構架構圖中,B-tree的索引行被存在索引頁中。在存儲葉子節點的頁中,這些行包含建立索引的數據(鍵)和指向表行的指針(TIDs)。在存儲分支節點和根節點的頁中,每行引用索引的一個子頁,並包含該頁中的最小值。
下面是一個使用整數鍵的字段索引的簡化示例。
索引的第一頁是一個元頁,它引用索引根。內部節點位於根的下面,而葉頁位於最底部行。向下箭頭表示從葉節點到表行(TIDs)的引用。
B-trees 有以下重要特征:
- b樹是平衡的,也就是說,每個葉頁與根頁之間用相同數量的內部頁分隔。因此,搜索任何值都需要同樣的時間。
- b樹是多分支的,也就是說,每個頁面(通常為8 KB)包含許多(數百)個TID。因此,b樹的深度非常小,對於非常大的表,實際上可以達到4-5。
- 索引中的數據按非降序排序(頁面之間和每個頁面內部),相同級別的頁面通過雙向列表相互連接。因此,我們可以通過一個列表遍歷一個或另一個方向,而不必每次都返回到根,來獲得有序的數據集。
等值查詢
讓我們考慮在樹中通過條件“index -field = expression”來搜索一個值。比如說,查詢49。
搜索從根節點開始,我們需要確定要下降到哪個子節點。由於知道根節點(4,32,64)中的鍵,因此我們計算出子節點中的值范圍。由於32≤49 < 64,我們需要下降到第二個子節點。接下來,重復相同的過程,直到我們到達一個葉節點,從中可以獲得所需的TID。
非等值查詢
當通過條件“indexed-field ≤ expression”(或“indexed-field ≥ expression”)進行搜索時,我們首先通過相等條件“indexed-field = expression”在索引中找到一個值(如果有的話),然后按照適當的方向遍歷葉子頁直到最后。下圖以n ≤ 35為例:
">"和“<”以類似的方式,只是最初找到的值必須刪除。
范圍檢索
當搜索范圍“expression1≤indexd -field≤expression2”時,我們通過條件“indexd -field = expression1”找到一個值,然后在滿足條件“indexd -field≤expression2”時繼續遍歷葉子頁面;反之亦然:從第二個表達式開始,向相反的方向走,直到我們到達第一個表達式。下圖以23 ≤ n ≤ 64為例:
多列索引:
演示數據:
demo=# select * from aircrafts; aircraft_code | model | range ---------------+---------------------+------- 773 | Boeing 777-300 | 11100 763 | Boeing 767-300 | 7900 SU9 | Sukhoi SuperJet-100 | 3000 320 | Airbus A320-200 | 5700 321 | Airbus A321-200 | 5600 319 | Airbus A319-100 | 6700 733 | Boeing 737-300 | 4200 CN1 | Cessna 208 Caravan | 1200 CR2 | Bombardier CRJ-200 | 2700 (9 rows)
demo=# create index aircrafts_case_asc_model_desc_idx on aircrafts( (case when range < 4000 then 1 when range < 10000 then 2 else 3 end) ASC, model DESC); demo=# explain(costs off) select class, model from aircrafts_v order by class ASC, model DESC;
QUERY PLAN ----------------------------------------------------------------- Index Scan using aircrafts_case_asc_model_desc_idx on aircrafts (1 row)
使用多列索引時出現的另一個問題是索引中列出列的順序。對於B-tree,這個順序非常重要:頁面內的數據將按第一個字段排序,然后按第二個字段排序,依此類推。大概結構如下:
從這個圖表中可以清楚地看出,通過像“class = 3”(僅通過第一個字段進行搜索)或“class = 3 and model =‘波音777-300’”(通過兩個字段進行搜索)這樣的謂詞進行搜索將有效地工作。
然而,通過謂詞“model = 'Boeing 777-300'”進行搜索的效率要低得多:從根開始,我們不能確定要下降到哪一個子節點,因此,我們必須下降到所有的子節點。這並不意味着這樣的索引永遠不會被使用——它的效率是有爭議的。例如,如果我們有三個級別的飛機,每個級別有很多型號,我們將不得不瀏覽大約三分之一的索引,這可能效率還不如全表掃描;
因此在實際使用過程中如果既有class = 3,class = 3 and model =‘波音777-300’,model =‘波音777-300’三類查詢,一般為(class,model)和model建兩個索引。
NULLs
Btree支持用is null 和is not null掃描索引。
demo=# create index on flights(actual_arrival); demo=# explain(costs off) select * from flights where actual_arrival is null;
QUERY PLAN ------------------------------------------------------- Bitmap Heap Scan on flights Recheck Cond: (actual_arrival IS NULL) -> Bitmap Index Scan on flights_actual_arrival_idx Index Cond: (actual_arrival IS NULL) (4 rows)
空值位於葉節點的一端或另一端,這取決於索引是如何創建的(NULLS FIRST or NULLS LAST)。如果查詢包含排序,這一點很重要:如果SELECT命令在其order BY子句中指定的空值順序與構建索引指定的順序相同,則可以使用索引(NULLS FIRST or NULLS LAST)。