1. 程式人生 > 實用技巧 >Postgresql索引結構-Btree

Postgresql索引結構-Btree

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-fieldexpression”(或“indexed-fieldexpression”)進行搜尋時,我們首先通過相等條件“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)。