btree index (6)

meta page

btree indexの先頭ページにはmeta pageというデータがおかれ,rootノードの位置などを記録する。(btreeではrootページが分割されるときrootページが移動するので,その位置をmeta pageで管理している)

typedef struct BTMetaPageData
{
    uint32      btm_magic;      /* should contain BTREE_MAGIC */
    uint32      btm_version;    /* should contain BTREE_VERSION */
    BlockNumber btm_root;       /* current root location */
    uint32      btm_level;      /* tree level of the root page */
    BlockNumber btm_fastroot;   /* current "fast" root location */
    uint32      btm_fastlevel;  /* tree level of the "fast" root page */
} BTMetaPageData;

btm_magic, btm_versionには,nbtree.hで定義されているBTREE_MAGIC, BTREE_VERSIONが設定される。
btm_root, btm_levelは,rootページの位置とページの高さ(リーフページを0として,親になるほど大きくなる)が設定される。

btm_fastroot, btm_fastlevelにはfast rootページの位置と高さが設定される。PostgreSQLのbtree indexはデータが削除されていったときに,各levelの一番右のページは削除されないため,treeの高さが低くなることがない。大きくなったindexのデータを多量に削除したとき,treeが痩せ細った状態になり,rootからあるlevelのノードまではアイテムが1つしかない状態になる可能性がある。この場合,最初に分岐が始まるノードから検索を開始すればよいので,これをfast rootとしてmeta pageに保存している。_bt_searchなどから_bt_getrootでrootページを取得するときは,このfast rootが返される。データが再度登録されていきfast rootが分割されるときには,fast rootの位置は再調整される。

btree indexの先頭ページはページの先頭にPageHeaderData(24byte)に続いて,このBTMetaPageData(24byte)があり,ページの末尾にBTPageOpaqueData(16byte)がある。これ以外は使用されない領域であり,0で埋まっている。(少しもったいないような気がする)

ページ分割の処理(概要)

_bt_insertonpgでページの空き容量が足りない場合,ページを分割する。ページの分割は以下の順序で実行される。

(1)_bt_findsplitloc/_bt_checksplitlocでページを分割する位置を決める
分割後の左右のページが大体同じになるように位置を計算している。
ただし一番右のページを分割する場合は,右側のページの空き容量が多くなるようにしている。(タイムスタンプやシーケンス番号など,増加していくデータが登録される場合,右のページばかりにデータが追加されていくため)

(2)_bt_splitで分割する
新しいページを作成し,左右のページにアイテムを振り分ける。
既存のページが左のページになり,新しいページが右のページになる。

(3)_bt_insert_parentで親ページに新しいページを登録する
rootページの分割のときは,新しいrootページを作成しそこに左右のページを登録する。meta pageも修正する。
root以外の分割のときは,親ページに対して_bt_insertonpgを再帰的に実行して新しいページのアイテムを登録する。親ページも分割が必要な場合,再帰コールにより処理が繰り返される。
(分割が必要なくなるまで,_bt_insertonpg -> _bt_insert_parent -> _bt_insertonpg...と実行される)