postgresql12 b-tree v4空間上和性能上的優化

在 pg v11 和 v12 上 常見測試用例

CREATE TABLE rel (
a bigint NOT NULL,
b bigint NOT NULL
);

ALTER TABLE rel
ADD CONSTRAINT rel_pkey PRIMARY KEY (a, b);

CREATE INDEX rel_b_idx ON rel (b);

\d rel
Table "public.rel"
Column | Type | Collation | Nullable | Default
--------+--------+-----------+----------+---------
a | bigint | | not null | 
b | bigint | | not null | 
Indexes:
"rel_pkey" PRIMARY KEY, btree (a, b)
"rel_b_idx" btree (b)
  • 它確保“a”和“b” 兩字段的每種組合最多有一個條目。
  • 它可以加快與給定“b”相關的所有“a”的搜索速度。

加入測試數據

INSERT INTO rel (a, b)
   SELECT i, i / 10000
   FROM generate_series(1, 20000000) AS i;

/* 收集統計信息 */
VACUUM (ANALYZE) rel;

 

B-tree索引提高1:插入很多重複的索引和數值
當我們比較的b列索引的大小的第一個區別是顯而易見的:

v11:
\di+ rel_b_idx
                           List of relations
 Schema |    Name     | Type  |  Owner   | Table |  Size  | Description 
--------+-------------+-------+----------+-------+--------+-------------
 public | rel_b_idx | index | postgres | rel   | 545 MB | 
(1 row)
v12: \di+ rel_b_idx Schema | Name | Type | Owner | Table | Size | Description --------+-------------+-------+----------+-------+--------+------------- public | rel_b_idx | index | postgres | rel | 408 MB | (1 row)


v11 比 v12 還要大 33%

 

每一個b列在index發生10000次,因此會有很多葉子節點的所有密鑰是相同的(每個葉子節點可以包含幾百項)。

 

 

V12之前,葉子頁必須是分立的,有時是最右邊的葉子節點,但有時不是。最右邊的葉子節點總是朝着右端,

以優化單調遞增插入拆分。與此相反,其他葉子節點是在中間,其中浪費的空間分割。

 

 

 

與V12,該表的行的物理地址(“元組ID”或TID)是索引關鍵字的一部分,所以重複的索引條目存儲在表的順序。

這會造成這樣的條目索引掃描訪問的物理順序表,它可以是一個顯著的性能優勢,特別是在機械磁盤。

換句話說,重複索引條目的相關性將是完美的。而且,僅由重複的頁將在右端分裂,產生密集索引。

 

 

 

加入類似的優化多列索引,但它並不適用於我們的主鍵索引,因爲重複不是在第1列。

主鍵索引在V11和V12緊湊,因爲第一列是單調遞增的,所以葉頁拆分在最右邊的頁面總是發生。

PostgreSQL的已經有針對的優化。

 

B-tree索引提高2:內部索引頁面的壓縮存儲

對於主鍵索引的改進是不那麼明顯,因爲它們幾乎在尺寸在V11和V12相同。我們必須更深入的挖掘這裏。

首先,觀察指標,只有在這兩個V11和V12(塊緩存)掃描:

v11:
EXPLAIN (ANALYZE, BUFFERS, COSTS off, SUMMARY off, TIMING off)
S
SELECT a, b FROM rel
W
WHERE a = 420024 AND b = 42;



                          QUERY PLAN                           
-
---------------------------------------------------------------
 
 Index Only Scan using rel_pkey on rel (actual rows=1 loops=1)
 
   Index Cond: ((a = 420024) AND (b = 42))
 
   Heap Fetches: 0
 
   Buffers: shared hit=5
(
(4 rows)


v12:
EXPLAIN (ANALYZE, BUFFERS, COSTS off, SUMMARY off, TIMING off)
S
SELECT a, b FROM rel
W
WHERE a = 420024 AND b = 42;



                          QUERY PLAN                           
-
---------------------------------------------------------------
 
 Index Only Scan using rel_pkey on rel (actual rows=1 loops=1)
 
   Index Cond: ((a = 420024) AND (b = 42))
 
   Heap Fetches: 0
 
   Buffers: shared hit=4
(
(4 rows)

在v12中,將讀取少一(索引)的塊,這意味着該索引少一級。
由於索引的大小几乎相同,因此必須意味着內部頁面可以容納更多的索引條目。
在v12中,索引具有更大的扇出度。

 

如上所述,PostgreSQL的V12引入的TID作爲索引關鍵字,這會浪費在內部索引頁的空間過多量的一部分。

所以同一個commit引入的來自內部 Page “冗餘”索引屬性。該TID是多餘的,

因爲是從包含子句非鍵屬性(V11這些也從內部索引頁除去)。

不過,PostgreSQL的V12也可以截斷不需要的錶行識別這些指標的屬性。

在我們的主鍵索引,出價是一個冗餘列,並從內部索引頁,

從而節省了8個字節的每個索引條目空間。讓我們一起來看看與pageinspect擴展內部索引頁:

 v11:
SELECT * FROM bt_page_items('rel_pkey', 2550);



 itemoffset |    ctid    | itemlen | nulls | vars |                      data                       
-
------------+------------+---------+-------+------+-------------------------------------------------
 
          1 | (2667,88)  |      24 | f     | f    | cd 8f 0a 00 00 00 00 00 45 00 00 00 00 00 00 00
 
          2 | (2462,0)   |       8 | f     | f    | 
 
          3 | (2463,15)  |      24 | f     | f    | d6 c0 09 00 00 00 00 00 3f 00 00 00 00 00 00 00
 
          4 | (2464,91)  |      24 | f     | f    | db c1 09 00 00 00 00 00 3f 00 00 00 00 00 00 00
 
          5 | (2465,167) |      24 | f     | f    | e0 c2 09 00 00 00 00 00 3f 00 00 00 00 00 00 00
 
          6 | (2466,58)  |      24 | f     | f    | e5 c3 09 00 00 00 00 00 3f 00 00 00 00 00 00 00
 
          7 | (2467,134) |      24 | f     | f    | ea c4 09 00 00 00 00 00 40 00 00 00 00 00 00 00
 
          8 | (2468,25)  |      24 | f     | f    | ef c5 09 00 00 00 00 00 40 00 00 00 00 00 00 00
 
          9 | (2469,101) |      24 | f     | f    | f4 c6 09 00 00 00 00 00 40 00 00 00 00 00 00 00
 
         10 | (2470,177) |      24 | f     | f    | f9 c7 09 00 00 00 00 00 40 00 00 00 00 00 00 00
.
...
 
        205 | (2666,12)  |      24 | f     | f    | c8 8e 0a 00 00 00 00 00 45 00 00 00 00 00 00 00
(
(205 rows)


在數據輸入我們所看到的援助和出價字節。該實驗在 little-endian 機器上進行的,
所以在第6行的數目將是0x09C3E5和0x3F的或(十進制數)639973和63.每個索引條目是24個字節寬,這8個字節是所述元組報頭。

在 v12:
SELECT * FROM bt_page_items('rel_pkey', 2700);



 itemoffset |   ctid   | itemlen | nulls | vars |          data           
-
------------+----------+---------+-------+------+-------------------------
 
          1 | (2862,1) |      16 | f     | f    | ab 59 0b 00 00 00 00 00
 
          2 | (2576,0) |       8 | f     | f    | 
 
          3 | (2577,1) |      16 | f     | f    | 1f 38 0a 00 00 00 00 00
 
          4 | (2578,1) |      16 | f     | f    | 24 39 0a 00 00 00 00 00
 
          5 | (2579,1) |      16 | f     | f    | 29 3a 0a 00 00 00 00 00
 
          6 | (2580,1) |      16 | f     | f    | 2e 3b 0a 00 00 00 00 00
 
          7 | (2581,1) |      16 | f     | f    | 33 3c 0a 00 00 00 00 00
 
          8 | (2582,1) |      16 | f     | f    | 38 3d 0a 00 00 00 00 00
 
          9 | (2583,1) |      16 | f     | f    | 3d 3e 0a 00 00 00 00 00
 
         10 | (2584,1) |      16 | f     | f    | 42 3f 0a 00 00 00 00 00
.
...
 
        286 | (2861,1) |      16 | f     | f    | a6 58 0b 00 00 00 00 00
(
(286 rows)

 

該數據僅包含a列,因爲a列已經被截斷了。這減少了索引項的大小爲16,讓更多的條目適合索引頁上。

升級注意事項
由於索引存儲在V12被改變,新的B-tree索引第4版已經推出。

由於與pg_upgrade不改變數據文件升級,索引仍然會在3.0版本升級後。
PostgreSQL的V12可以使用這些指標,但上述的優化將不可用。
你需要重新索引的索引將其升級到4.0版本(這已經在PostgreSQL的V12變得更加容易與REINDEX兼)。

其他B-tree索引功能在推出V12 有PostgreSQL中V12添加了一些其他方面的改進。如下簡單列表: 1. 減少B樹索引插入,以提高性能鎖定開銷。 2. REINDEX CONCURRENTLY,重建無停機時間的索引。
3. 完善與許多屬性的索引僅索引掃描性能。
4. 添加視圖 pg_stat_progress_create_index 報到CREATE INDEX和REINDEX進展。

總結 擁有許多重複的條目索引, V12 更有優勢 , 推薦 pg_upgrade後用 REINDEX CONCURRENTLY 重新索引。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章