PG12-2 B-Tree 索引 level 0 root頁

qiqihappy發表於2022-04-10

接上節,依然使用上節的用例

如下

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=#

可以看到root page id = 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    |          4 |          0 |            16 |      8192 |      8068 |         0 |         0 |    0 |          3
(1 row)
postgres=# select * from bt_page_items('pk_t_index',1);
 itemoffset | ctid  | itemlen | nulls | vars |          data           
------------+-------+---------+-------+------+-------------------------
          1 | (0,1) |      16 | f     | f    | 02 00 00 00 00 00 00 00
          2 | (0,2) |      16 | f     | f    | 04 00 00 00 00 00 00 00
          3 | (0,3) |      16 | f     | f    | 08 00 00 00 00 00 00 00
          4 | (0,4) |      16 | f     | f    | 10 00 00 00 00 00 00 00
(4 rows)
postgres=# select * from heap_page_items(get_raw_page('pk_t_index',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 |   8160 |        1 |     16 |        |        |          |        |             |            |        |        |       | 
  2 |   8144 |        1 |     16 |        |        |          |        |             |            |        |        |       | 
  3 |   8128 |        1 |     16 |        |        |          |        |             |            |        |        |       | 
  4 |   8112 |        1 |     16 |        |        |          |        |             |            |        |        |       | 
(4 rows)
postgres=# 
postgres=# select * from page_header(get_raw_page('pk_t_index',1));
    lsn    | checksum | flags | lower | upper | special | pagesize | version | prune_xid 
-----------+----------+-------+-------+-------+---------+----------+---------+-----------
 0/4D402D0 |        0 |     0 |    40 |  8112 |    8176 |     8192 |       4 |         0
(1 row)

根據root page id = 1檢視root page的stats

btpo=0 說明已經到了最底層

btpo_flags=3,說明它既是leaf又是root頁。

btpo_prev和btpo_next分別表示該頁的相鄰頁(branch page是雙向連結串列)。

btpo_flags 可以在程式碼中檢視(src/include/access/nbtree.h),一共有幾個

typedef struct BTPageOpaqueData
{
    BlockNumber btpo_prev;      /* left sibling, or P_NONE if leftmost */
    BlockNumber btpo_next;      /* right sibling, or P_NONE if rightmost */
    union
    {
        uint32      level;      /* tree level --- zero for leaf pages */
        TransactionId xact;     /* next transaction ID, if deleted */
    }           btpo;
    uint16      btpo_flags;     /* flag bits, see below */
    BTCycleId   btpo_cycleid;   /* vacuum cycle ID of latest split */
} BTPageOpaqueData;
/* Bits defined in btpo_flags */
#define BTP_LEAF        (1 << 0)    /* leaf page, i.e. not internal page */
#define BTP_ROOT        (1 << 1)    /* root page (has no parent) */
#define BTP_DELETED     (1 << 2)    /* page has been deleted from tree */
#define BTP_META        (1 << 3)    /* meta-page */
#define BTP_HALF_DEAD   (1 << 4)    /* empty, but still in tree */
#define BTP_SPLIT_END   (1 << 5)    /* rightmost page of split group */
#define BTP_HAS_GARBAGE (1 << 6)    /* page has LP_DEAD tuples */
#define BTP_INCOMPLETE_SPLIT (1 << 7)   /* right sibling's downlink is missing */

BTPageOpaqueData儲存在page 1(這裡的page 1又是根節點,又是葉節點,還是子節點,所以不好說,是否每個節點頁都還包含這個資料結構,在下面的實驗中會體現)sepcial空間,如下

page1相對整個頁面的偏移為8192,而page 1的special空間相對頁頭偏移為8176,所以相對這個檔案偏移為16368

postgres@morro:~$ hexdump -C /var/lib/postgresql/data/pg12/base/12711/46918 -s 16368 -n 16    
00003ff0  00 00 00 00 00 00 00 00  00 00 00 00 03 00 00 00  |................|
00004000
postgres@morro:~$

BTPageOpaqueData大小正好為16位元組,這裡不一一檢視每個域的值了,可以看到其他均為0,flags為3.

下面檢視一下,bt元組的items

先插入兩條新元組,先插入id為18,再插入id為17

postgres=# insert into t_index values(18,'4','d');
INSERT 0 1
postgres=# insert into t_index values(17,'4','d');
INSERT 0 1
postgres=# 
postgres=# select * from bt_page_items('pk_t_index',1);
 itemoffset | ctid  | itemlen | nulls | vars |          data           
------------+-------+---------+-------+------+-------------------------
          1 | (0,1) |      16 | f     | f    | 02 00 00 00 00 00 00 00
          2 | (0,2) |      16 | f     | f    | 04 00 00 00 00 00 00 00
          3 | (0,3) |      16 | f     | f    | 08 00 00 00 00 00 00 00
          4 | (0,4) |      16 | f     | f    | 10 00 00 00 00 00 00 00
          5 | (0,6) |      16 | f     | f    | 11 00 00 00 00 00 00 00
          6 | (0,5) |      16 | f     | f    | 12 00 00 00 00 00 00 00
(6 rows)
postgres=#

可以看到,實際資料儲存時是無序的(索性必須是有序的),但是索引的行指標是有序的

18對應的ctid為(0,5),17對應的ctid為(0,6),索引17存在18後面,所以是無序的,

但是(0,5)也就是18對應的行指標為lp6,(0,6)對應的行指標為lp5,所以行指標是有序的,正確的表示了索引值的大小。

透過這個例子,也就是說如果我們插入一個15的,15對應的ctid應該為(0,7)但是對應的行指標應該為lp4,10,17,18對應的行指標應該要後移一步。試一下

postgres=# select * from bt_page_items('pk_t_index',1);
 itemoffset | ctid  | itemlen | nulls | vars |          data           
------------+-------+---------+-------+------+-------------------------
          1 | (0,1) |      16 | f     | f    | 02 00 00 00 00 00 00 00
          2 | (0,2) |      16 | f     | f    | 04 00 00 00 00 00 00 00
          3 | (0,3) |      16 | f     | f    | 08 00 00 00 00 00 00 00
          4 | (0,7) |      16 | f     | f    | 0f 00 00 00 00 00 00 00
          5 | (0,4) |      16 | f     | f    | 10 00 00 00 00 00 00 00
          6 | (0,6) |      16 | f     | f    | 11 00 00 00 00 00 00 00
          7 | (0,5) |      16 | f     | f    | 12 00 00 00 00 00 00 00
(7 rows)
postgres=#

結果和我們想的一樣。

我們再來解析一條item

bt元組的頭資訊如下,長度為8個位元組

typedef struct IndexTupleData
{
    ItemPointerData t_tid;      /* reference TID to heap tuple */
    /* ---------------
     * t_info is laid out in the following fashion:
     *
     * //該元組是否為null
     * 15th (high) bit: has nulls
     * //該元組是否有可變長度的屬性
     * 14th bit: has var-width attributes
     * //access method 定義其意義
     * 13th bit: AM-defined meaning
     * //元組的大小
     * 12-0 bit: size of tuple
     * ---------------
     */
    unsigned short t_info;      /* various info about tuple */
} IndexTupleData;
typedef struct ItemPointerData
{
    BlockIdData ip_blkid;  //塊號
    OffsetNumber ip_posid; //塊內偏移
}
typedef struct BlockIdData
{
    uint16      bi_hi;
    uint16      bi_lo;
} BlockIdData;

檢視所有bt元組在頁內的偏移

postgres=# select * from heap_page_items(get_raw_page('pk_t_index',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 |   8160 |        1 |     16 |        |        |          |        |             |            |        |        |       | 
  2 |   8144 |        1 |     16 |        |        |          |        |             |            |        |        |       | 
  3 |   8128 |        1 |     16 |        |        |          |        |             |            |        |        |       | 
  4 |   8064 |        1 |     16 |        |        |          |        |             |            |        |        |       | 
  5 |   8112 |        1 |     16 |        |        |          |        |             |            |        |        |       | 
  6 |   8080 |        1 |     16 |        |        |          |        |             |            |        |        |       | 
  7 |   8096 |        1 |     16 |        |        |          |        |             |            |        |        |       | 
(7 rows)
postgres=#

以第一條元組為例,相對於檔案頭,該元組的偏移為8192+8160,長度為16位元組,其中8位元組為頭,

Tcid

postgres@morro:~$ hexdump -C /var/lib/postgresql/data/pg12/base/12711/46918 -s 16352 -n 4
00003fe0  00 00 00 00                                       |....|
00003fe4
postgres@morro:~$

對應(0, 1)中的0,注意這裡的t-cid指向的是堆表的元組位置,在本例中是t_index的page 0 的 1號元組。

postgres@morro:~$ hexdump -C /var/lib/postgresql/data/pg12/base/12711/46918 -s 16356 -n 2 
00003fe4  01 00                                             |..|
00003fe6
postgres@morro:~$

1,對應(0,1)中的1.

t_Info

postgres@morro:~$ hexdump -C /var/lib/postgresql/data/pg12/base/12711/46918 -s 16358 -n 2 
00003fe6  10 00                                             |..|
00003fe8
postgres@morro:~$

0X0010,根據上面的解釋,表示元組長度為16

元組的資訊為

postgres=# select * from bt_page_items('pk_t_index',1);
 itemoffset | ctid  | itemlen | nulls | vars |          data           
------------+-------+---------+-------+------+-------------------------
          1 | (0,1) |      16 | f     | f    | 02 00 00 00 00 00 00 00
          2 | (0,2) |      16 | f     | f    | 04 00 00 00 00 00 00 00
          3 | (0,3) |      16 | f     | f    | 08 00 00 00 00 00 00 00
          4 | (0,7) |      16 | f     | f    | 0f 00 00 00 00 00 00 00
          5 | (0,4) |      16 | f     | f    | 10 00 00 00 00 00 00 00
          6 | (0,6) |      16 | f     | f    | 11 00 00 00 00 00 00 00
          7 | (0,5) |      16 | f     | f    | 12 00 00 00 00 00 00 00
(7 rows)
postgres=#

檢視第一條元組的資料

postgres@morro:~$ hexdump -C /var/lib/postgresql/data/pg12/base/12711/46918 -s 16360 -n 8  
00003fe8  02 00 00 00 00 00 00 00                           |........|
00003ff0
postgres@morro:~$

根據原始碼註釋如下

/*
 * Index tuple header structure
 *
 * All index tuples start with IndexTupleData.  If the HasNulls bit is set,
 * this is followed by an IndexAttributeBitMapData.  The index attribute
 * values follow, beginning at a MAXALIGN boundary.
 *
 * Note that the space allocated for the bitmap does not vary with the number
 * of attributes; that is because we don't have room to store the number of
 * attributes in the header.  Given the MAXALIGN constraint there's no space
 * savings to be had anyway, for usual values of INDEX_MAX_KEYS.
 */
typedef struct IndexTupleData
{
    ItemPointerData t_tid;      /* reference TID to heap tuple */
    /* ---------------
     * t_info is laid out in the following fashion:
     *
     * //該元組是否為null
     * 15th (high) bit: has nulls
     * //該元組是否有可變長度的屬性
     * 14th bit: has var-width attributes
     * //access method 定義其意義
     * 13th bit: AM-defined meaning
     * //元組的大小
     * 12-0 bit: size of tuple
     * ---------------
     */
    unsigned short t_info;      /* various info about tuple */
} IndexTupleData;  
typedef struct IndexAttributeBitMapData
{
    bits8       bits[(INDEX_MAX_KEYS + 8 - 1) / 8];
}           IndexAttributeBitMapData;

所以如果元組為null,才會跟上 IndexAttributeBitMapData


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70012752/viewspace-2886383/,如需轉載,請註明出處,否則將追究法律責任。

相關文章