PG 12-2 B-Tree 索引 分析 分裂 level = 1
感謝大神的分享,如下,
https://www.cnblogs.com/xueqiuqiu/articles/10994404.html
透過上面的分析,我們可以思考一下,一個root page能夠存多少個item
我們知道B+樹的基本資料結構,root page 就是B+樹的頂層。
在我們的實驗中,一個索引元組的大小為16,每個索引元組都有一個行指標,4位元組,頁大小為8192,頁頭24位元組,special空間為16,那麼理論上可以存(8192-24-16)/(16+4)=407個元組
來驗證一下,
postgres=# drop table if exists t_index; DROP TABLE postgres=# vacuum full; VACUUM postgres=# create table t_index (id int,c1 char(8),c2 varchar(16)); CREATE TABLE postgres=# alter table t_index add constraint pk_t_index primary key(id); ALTER TABLE postgres=# do $$ postgres$# begin postgres$# for i in 1..407 loop postgres$# insert into t_index (id, c1, c2) values (i, '#'||i||'#', '#'||i||'#'); postgres$# end loop; postgres$# end $$; DO postgres=#
我們插入407個元組
檢視一下索引表,如下
postgres=# select * from bt_metap('pk_t_index'); magic | version | root | level | fastroot | fastlevel | oldest_xact | last_cleanup_num_tuples --------+---------+------+-------+----------+-----------+-------------+------------------------- 340322 | 4 | 1 | 0 | 1 | 0 | 0 | -1 (1 row) postgres=# select * from bt_page_stats('pk_t_index',1); blkno | type | live_items | dead_items | avg_item_size | page_size | free_size | btpo_prev | btpo_next | btpo | btpo_flags -------+------+------------+------------+---------------+-----------+-----------+-----------+-----------+------+------------ 1 | l | 407 | 0 | 16 | 8192 | 8 | 0 | 0 | 0 | 3 (1 row) postgres=# select * from page_header(get_raw_page('pk_t_index',1)); lsn | checksum | flags | lower | upper | special | pagesize | version | prune_xid -----------+----------+-------+-------+-------+---------+----------+---------+----------- 0/5E73F38 | 0 | 0 | 1652 | 1664 | 8176 | 8192 | 4 | 0 (1 row)
這個時候還沒有分層,這裡的索引有序性是透過行指標來實現的。
現在插入一條新資料
postgres=# insert into t_index values(408, '$$408', '##408##'); INSERT 0 1 postgres=#
檢視索引表
postgres=# select * from bt_metap('pk_t_index'); magic | version | root | level | fastroot | fastlevel | oldest_xact | last_cleanup_num_tuples --------+---------+------+-------+----------+-----------+-------------+------------------------- 340322 | 4 | 3 | 1 | 3 | 1 | 0 | -1 (1 row) postgres=# select * from bt_page_stats('pk_t_index',1); blkno | type | live_items | dead_items | avg_item_size | page_size | free_size | btpo_prev | btpo_next | btpo | btpo_flags -------+------+------------+------------+---------------+-----------+-----------+-----------+-----------+------+------------ 1 | l | 367 | 0 | 16 | 8192 | 808 | 0 | 2 | 0 | 1 (1 row) postgres=# select * from bt_page_stats('pk_t_index',2); blkno | type | live_items | dead_items | avg_item_size | page_size | free_size | btpo_prev | btpo_next | btpo | btpo_flags -------+------+------------+------------+---------------+-----------+-----------+-----------+-----------+------+------------ 2 | l | 42 | 0 | 16 | 8192 | 7308 | 1 | 0 | 0 | 1 (1 row) postgres=# select * from bt_page_stats('pk_t_index',3); blkno | type | live_items | dead_items | avg_item_size | page_size | free_size | btpo_prev | btpo_next | btpo | btpo_flags -------+------+------------+------------+---------------+-----------+-----------+-----------+-----------+------+------------ 3 | r | 2 | 0 | 12 | 8192 | 8116 | 0 | 0 | 1 | 2 (1 row) postgres=#
可以看到分層了,除root外,還有一層(level = 1),page 1 為左子樹(btpo_next = 2),page 2 為右子樹(btpo_prev=1),其中page 1 有367條記錄,page 2有42條記錄,總共409條元組,符合B+樹所有的索引都儲存在葉子節點的定義,這裡比408條多一條,下面會解釋多的一條是什麼
同樣的檢視一下metapage
如下
postgres=# select * from page_header(get_raw_page('pk_t_index',0)); lsn | checksum | flags | lower | upper | special | pagesize | version | prune_xid -----------+----------+-------+-------+-------+---------+----------+---------+----------- 0/5E8CEF8 | 0 | 0 | 64 | 8176 | 8176 | 8192 | 4 | 0 (1 row) postgres=#
可以發現,空間使用情況和分層之前沒有區別,當然其中資料是肯定發生了變化,這裡就不贅述了。
接下去,看一下root page
postgres=# select * from bt_page_items('pk_t_index',3); itemoffset | ctid | itemlen | nulls | vars | data ------------+-------+---------+-------+------+------------------------- 1 | (1,0) | 8 | f | f | 2 | (2,1) | 16 | f | f | 6f 01 00 00 00 00 00 00 (2 rows) postgres=# select * from bt_page_stats('pk_t_index',3); blkno | type | live_items | dead_items | avg_item_size | page_size | free_size | btpo_prev | btpo_next | btpo | btpo_flags -------+------+------------+------------+---------------+-----------+-----------+-----------+-----------+------+------------ 3 | r | 2 | 0 | 12 | 8192 | 8116 | 0 | 0 | 1 | 2 (1 row) postgres=#
這裡root節點有兩個item,其中一個指向最左邊的葉節點,不需要儲存最小值,對於這一點,另一個指向右葉節點,需要儲存右節點的最小值,又稱為儲存右鏈路,這裡的值為367
對於這一點的理解,是這樣的,我們思考一下在本例中查詢一個索引id,因為本例只有兩個葉節點,那麼只需要判斷索引id是大於367,還是小於367,如果是小於367,那麼找左葉節點,如果大於367,找右葉節點,如果=367,根據citd知道,2號page的1號元組中儲存的ctid,也就是(2,53),如下
postgres=# select * from bt_page_items('pk_t_index',2) limit 10; itemoffset | ctid | itemlen | nulls | vars | data ------------+--------+---------+-------+------+------------------------- 1 | (2,53) | 16 | f | f | 6f 01 00 00 00 00 00 00 2 | (2,54) | 16 | f | f | 70 01 00 00 00 00 00 00 3 | (2,55) | 16 | f | f | 71 01 00 00 00 00 00 00 4 | (2,56) | 16 | f | f | 72 01 00 00 00 00 00 00 5 | (2,57) | 16 | f | f | 73 01 00 00 00 00 00 00 6 | (2,58) | 16 | f | f | 74 01 00 00 00 00 00 00 7 | (2,59) | 16 | f | f | 75 01 00 00 00 00 00 00 8 | (2,60) | 16 | f | f | 76 01 00 00 00 00 00 00 9 | (2,61) | 16 | f | f | 77 01 00 00 00 00 00 00 10 | (2,62) | 16 | f | f | 78 01 00 00 00 00 00 00 (10 rows)
然後在找堆表的2號page的53號元組,如下
postgres=# select * from heap_page_items(get_raw_page('t_index',2)) where t_ctid = '(2,53)'; lp | lp_off | lp_flags | lp_len | t_xmin | t_xmax | t_field3 | t_ctid | t_infomask2 | t_infomask | t_hoff | t_bits | t_oid | t_data ----+--------+----------+--------+--------+--------+----------+--------+-------------+------------+--------+--------+-------+------------------ ------------------------ 53 | 5648 | 1 | 43 | 902 | 0 | 366 | (2,53) | 3 | 2306 | 24 | | | \x6f0100001323333 637232020200d2333363723 (1 row) postgres=#
所以不用儲存最小值。
這裡不去對root page 的special空間進行檢視,
兩個關鍵欄位
btpo = 1 說明還沒有到最底層(最底層btpo=0, 這種頁裡面儲存的ctid才代表指向heap page的地址)
btpo_flags=2 說明這個頁是root page
再來看一下,
索引page 1
postgres=# select * from bt_page_stats('pk_t_index',1); blkno | type | live_items | dead_items | avg_item_size | page_size | free_size | btpo_prev | btpo_next | btpo | btpo_flags -------+------+------------+------------+---------------+-----------+-----------+-----------+-----------+------+------------ 1 | l | 367 | 0 | 16 | 8192 | 808 | 0 | 2 | 0 | 1 (1 row) postgres=# select * from bt_page_items('pk_t_index',1) limit 10; itemoffset | ctid | itemlen | nulls | vars | data ------------+-------+---------+-------+------+------------------------- 1 | (2,1) | 16 | f | f | 6f 01 00 00 00 00 00 00 2 | (0,1) | 16 | f | f | 01 00 00 00 00 00 00 00 3 | (0,2) | 16 | f | f | 02 00 00 00 00 00 00 00 4 | (0,3) | 16 | f | f | 03 00 00 00 00 00 00 00 5 | (0,4) | 16 | f | f | 04 00 00 00 00 00 00 00 6 | (0,5) | 16 | f | f | 05 00 00 00 00 00 00 00 7 | (0,6) | 16 | f | f | 06 00 00 00 00 00 00 00 8 | (0,7) | 16 | f | f | 07 00 00 00 00 00 00 00 9 | (0,8) | 16 | f | f | 08 00 00 00 00 00 00 00 10 | (0,9) | 16 | f | f | 09 00 00 00 00 00 00 00 (10 rows) postgres=# postgres=# select count(*) from bt_page_items('pk_t_index',1); count ------- 367 (1 row) postgres=#
這裡可以看到,page 1 有右兄弟節點,所以它的第一條item指向右兄弟節點的第一個元組,這符合B+樹的設計,葉節點和banch節點是一個雙向連結串列,其實我們進一步確定,如下
postgres=# select * from bt_page_items('pk_t_index',1) where ctid='(2,1)'; itemoffset | ctid | itemlen | nulls | vars | data ------------+-------+---------+-------+------+------------------------- 1 | (2,1) | 16 | f | f | 6f 01 00 00 00 00 00 00 316 | (2,1) | 16 | f | f | 3b 01 00 00 00 00 00 00 (2 rows) postgres=#
可以看到,page 1 的ctid為(2,1)的記錄有兩條,進一步檢查
postgres=# select * from heap_page_items(get_raw_page('t_index',2)) where t_ctid = '(2,1)'; lp | lp_off | lp_flags | lp_len | t_xmin | t_xmax | t_field3 | t_ctid | t_infomask2 | t_infomask | t_hoff | t_bits | t_oid | t_data ----+--------+----------+--------+--------+--------+----------+--------+-------------+------------+--------+--------+-------+------------------ ------------------------ 1 | 8144 | 1 | 43 | 902 | 0 | 314 | (2,1) | 3 | 2306 | 24 | | | \x3b0100001323333 135232020200d2333313523 (1 row) postgres=# select * from bt_page_items('pk_t_index',2) limit 5; itemoffset | ctid | itemlen | nulls | vars | data ------------+--------+---------+-------+------+------------------------- 1 | (2,53) | 16 | f | f | 6f 01 00 00 00 00 00 00 2 | (2,54) | 16 | f | f | 70 01 00 00 00 00 00 00 3 | (2,55) | 16 | f | f | 71 01 00 00 00 00 00 00 4 | (2,56) | 16 | f | f | 72 01 00 00 00 00 00 00 5 | (2,57) | 16 | f | f | 73 01 00 00 00 00 00 00 (5 rows) postgres=#
可以看到,的確page 1的第一個items指向的是page 2的第一個元組,這麼做的意義是,假設要查詢 id > 100的元組,那麼從root開始,走左子樹,找到data等於100的bt元組的,行指標,該行指標之後所有的元組都是大於100的(因為行指標是排好序的),那麼對於右兄弟節點上的所有資料,需要找到一個入口,這個入口就是1號item對應的地址。
再看一下,page 2
postgres=# select * from bt_page_stats('pk_t_index',2); blkno | type | live_items | dead_items | avg_item_size | page_size | free_size | btpo_prev | btpo_next | btpo | btpo_flags -------+------+------------+------------+---------------+-----------+-----------+-----------+-----------+------+------------ 2 | l | 42 | 0 | 16 | 8192 | 7308 | 1 | 0 | 0 | 1 (1 row) postgres=# select * from bt_page_items('pk_t_index',2) limit 10; itemoffset | ctid | itemlen | nulls | vars | data ------------+--------+---------+-------+------+------------------------- 1 | (2,53) | 16 | f | f | 6f 01 00 00 00 00 00 00 2 | (2,54) | 16 | f | f | 70 01 00 00 00 00 00 00 3 | (2,55) | 16 | f | f | 71 01 00 00 00 00 00 00 4 | (2,56) | 16 | f | f | 72 01 00 00 00 00 00 00 5 | (2,57) | 16 | f | f | 73 01 00 00 00 00 00 00 6 | (2,58) | 16 | f | f | 74 01 00 00 00 00 00 00 7 | (2,59) | 16 | f | f | 75 01 00 00 00 00 00 00 8 | (2,60) | 16 | f | f | 76 01 00 00 00 00 00 00 9 | (2,61) | 16 | f | f | 77 01 00 00 00 00 00 00 10 | (2,62) | 16 | f | f | 78 01 00 00 00 00 00 00 (10 rows)
可以看到,page 2的所有item均為索引元組,其與左兄弟節點的關係,透過btpo_prev體現。
可以想象,如果,有3個葉節點,中間的節點,頁一定會有一個指向右兄弟節點的item
實際上上述的部落格中,都提到有,這裡不贅述了,可以參考上述部落格
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70012752/viewspace-2886389/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 資料庫索引分裂 問題分析資料庫索引
- oracle的B-tree索引結構分析Oracle索引
- PostgreSQL DBA(139) - PG 12(B-tree index improvement 1#)SQLIndex
- 索引分裂的enq索引ENQ
- PostgreSQL的B-tree索引SQL索引
- Oracle索引分裂(Index Block Split)Oracle索引IndexBloC
- 分析函式學習1 level函式
- MySQL探索(一):B-Tree索引MySql索引
- 平衡樹索引(b-tree index)索引Index
- MySQL Hash索引和B-Tree索引的區別MySql索引
- Oracle索引塊分裂split資訊彙總Oracle索引
- PostgreSQL Page頁結構解析(5)- B-Tree索引儲存結構#1SQL索引
- Oracle vs PG 索引資訊Oracle索引
- Oracle index索引塊分裂split資訊彙總OracleIndex索引
- 【索引】Bitmap點陣圖索引與普通的B-Tree索引鎖的比較索引
- 包含列的索引:SQL Server索引進階 Level 5索引SQLServer
- ORACLE 12c索引分裂引起的會話夯Oracle索引會話
- B-Tree索引與Bitmap點陣圖索引的鎖代價比較研究索引
- pg日誌分析
- 【MySQL(1)| B-tree和B+tree】MySql
- 索引分裂造成的index contention等待事件的診斷索引Index事件
- 【Bitmap Index】B-Tree索引與Bitmap點陣圖索引的鎖代價比較研究Index索引
- [20211105]索引分裂塊清除日誌增加(唯一索引).txt索引
- 【ASK_ORACLE】關於Oracle索引分裂你需要知道的Oracle索引
- Oracle中B-Tree、Bitmap和函式索引使用案例總結Oracle函式索引
- 【PG】ora2pg 分別匯出表,索引,儲存過程等索引儲存過程
- [20210604]索引分裂與 itl ktbitflg.txt索引
- 關於insert操作造成索引葉節點分裂的驗證索引
- ceph-pg雜湊分析
- B-tree
- PostgreSQL Page頁結構解析(6)- B-Tree索引儲存結構#2SQL索引
- PostgreSQL Page頁結構解析(7)- B-Tree索引儲存結構#3SQL索引
- 研究 b-tree 索引結構的指令碼 (文件 ID 1577374.1)索引指令碼
- postgreSQL 12-2 vacuum-主流程SQL
- [20211105]索引分裂 塊清除 日誌增加.txt索引
- [20211108]索引分裂塊清除日誌增加(唯一索引)2.txt索引
- 索引分析索引
- XML進階:Level 1 – XML簡介XML