btree index (5)
_bt_moveright
例えば,更新トランザクション(UPD-Tr)と検索トランザクション(SEL-Tr)が,以下のように並行動作した場合,
UPD-Tr: 親ノードP0を検索し,リーフページP1のポインタを取得 SEL-Tr: 親ノードP0を検索し,リーフページP1のポインタを取得 UPD-Tr: ページP1のWrite lockを取得 UPD-Tr: ページP1の空き容量が足りない場合、P1を分割して更新 UPD-Tr: ページP1のWrite lockを開放 SEL-Tr: ページP1のRead lockを取得
SEL-TrがページP1のRead lockを取得する前にページが分割されたので,ページP1に検索すべきデータが存在しないかもしれない。そのため,Read lockを取得したあと正しい位置まで右に移動する必要がある。
現在のページに目的のデータが存在するかどうかは,検索キーとページのhigh keyを比較してチェックする。もし検索キーのほうが大きい場合,ページが分割されたので右に移動する。
while (!P_RIGHTMOST(opaque) && (P_IGNORE(opaque) || _bt_compare(rel, keysz, scankey, page, P_HIKEY) >= cmpval)) { /* step right one page */ BlockNumber rblkno = opaque->btpo_next;
IndexTupleの構造
IndexTupleの構造はitup.hで定義されている。
typedef struct IndexTupleData { ItemPointerData t_tid; /* reference TID to heap tuple */ /* --------------- * t_info is layed out in the following fashion: * * 15th (high) bit: has nulls (NULLデータがある) * 14th bit: has var-width attributes (可変長データがある) * 13th bit: unused (未使用) * 12-0 bit: size of tuple (IndexTupleのサイズ) * --------------- */ unsigned short t_info; /* various info about tuple */ } IndexTupleData; /* MORE DATA FOLLOWS AT END OF STRUCT */ typedef IndexTupleData *IndexTuple;
t_tidにテーブルのタプルへのリファレンス情報が設定される。t_infoにはIndexTupleのサイズなどの情報が設定される。
t_infoの後にデータが続く。IndexTupleのデータにNULLがある場合,まずNULLビットマップ(IndexAttributeBitMapData)が続き,その後にTupleのデータが続く。IndexTupeにNULLが無い場合はNULLビットマップはない。
NULLビットマップは,Heap Tuple(テーブルのタプル)のNULLビットマップと異なり,固定長になっている。
typedef struct IndexAttributeBitMapData { bits8 bits[(INDEX_MAX_KEYS + 8 - 1) / 8]; } IndexAttributeBitMapData; typedef IndexAttributeBitMapData *IndexAttributeBitMap;
デフォルトではINDEX_MAX_KEYS = 32なので,IndexAttributeBitMapDataは4byteになる。
Tupleのデータ構造は,HeapTupleと同じ。データを作成する処理も同じ内部APIを使用している。
BTPageOpaque
btree indexの各ページが保持するデータ。
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 */ } BTPageOpaqueData; typedef BTPageOpaqueData *BTPageOpaque;
btpo_prev/btpo_nextで左右のページへのリンクを持つ。これらはindex scanで左右に移動するときに使われる。また,右のページへのリンクは,別のトランザクションがページを分割したときに,正しいページを探すときにも使われる。(この処理は_bt_moverightで実行される)
levelにはツリーの高さの位置が設定される。リーフページは0でrootに向かって増えていく。xaxtはページが削除されたときのnext transaction IDが設定される。btpo_flagsにはページの状態を表すフラグが設定される。以下のフラグがある。
BTP_LEAF: リーフページ BTP_ROOT: ルートページ BTP_DELETED: ツリーから削除されたページ BTP_META: metaページ BTP_HALF_DEAD: 空ページだがツリーには存在する
BTPageOpaqueデータはページの末尾に存在し,そのポインタを取得するには,PageGetSpecialPointerを使う。
BTPageOpaque opaque = (BTPageOpaque) PageGetSpecialPointer(page);
メモ:もしlevelがリーフページまでの距離を正確に表しているなら、_bt_searchでstackのメモリ割り当てをまとめて実行でき、palloc/pfreeの実行回数を減らせる。
PageInit
ページを初期化する。
(1)ページ全体を0で初期化する
(2)ページの先頭からPageHeaderの領域を確保する(pd_lowerを設定)
(3)ページの末尾からSpecialDataの領域を確保する(pd_upper, pd_specialを設定)
SpecialDataは使用するオブジェクトにより異なり,btree indexの場合はBTPageOpaqueDataになる。
(4)ページのサイズ,バージョンを設定する