pg的btree索引有4中類型的索引頁面:1、meta page,每個索引都會有該頁面,這個頁面直接指向root page。2、root page頁面,如果heap item很多,會指向新的branch page或者是leaf page。3、branch page頁面指向branch page或者leaf page。4、leaf page。
我在9.4.7的版本上8kblock的int類型字段上的索引,大概一個頁面可以存407條記錄,也就是說,如果你是int索引且隻有一個字段記錄數在0-407範圍内是隻有root page的這是1級結構,2級結構的記錄數就是407*407,3級結構就是407^3,以此類推。
現在具體開始使用pageinspect分析pg索引結構。先安裝pageinspect

建立好測試表,插入測試資料。
下面開始分析1級結構。找到表a的索引名稱
使用bt_metap看索引頁面資訊。
這個資訊意思就是a_pkey這個索引level=0就說明隻有meta page和root page,root page的id是1。
使用bt_page_stats看下索引頁面的狀态資訊。
btpo_flags=2表示root節點,btpo_flags=1表示 leaf節點,btpo_flags=0表示 branch節點,btpo_flags=3表示即使root節點又是leaf節點。btpo=0是最底層,btpo_prev和btpo_next表示左右節點頁面号。
使用bt_page_items看下索引頁面内容。
這裡面有個注意點,如果該節點是最右節點就是第一條記錄就是指向頁面的第一條記錄,如果不是最右節點就是第二條為第一條記錄,第一條記錄為右節點的起始item。從bt_page_stats可以看出它的左右節點都是0号頁面也就是meta page是以1号頁面是最右節點,是以取第一條記錄,ctid(0,1)
二級結構。
level=1代表2級結構,就是meta page,root page,leaf page。root頁面id=3。
bt_page_stats資訊
btpo_flags=2表示這是root節點,btpo=1表示不是最底層,左右節點都是0号頁面。
bt_page_items資訊
因為這個是最右節點是以是第一條記錄指向下一個節點頁面号。是以是(1,1)
看下1号頁面的bt_page_stats資訊
btpo_flags=1是leaf page,btpo=0是最底層。左節點頁面号是0,右節點頁面号是2。該節點不是最右節點。
看下1号頁面bt_page_items資訊
因為不是最右節點,是以取第二條記錄(0,1)。
看下(0,1)的具體内容。
三級結構。
level=2說明有meta page,root page,一個branch page,一個leaf page,root page頁面号是412。
btpo_flags=2是root節點,btpo=2不是最底層。
branch節點頁面号是3。
branch節點bt_page_stats。
btpo_flags=0,是branch節點,btpo=1不是底層節點。
branch節點bt_page_items。
下一葉子節點頁面号是(1,1)。
leaf節點bt_page_stats。
btpo=0,是底層節點。btpo_flags=1是leaf page。
leaf節點bt_page_items。
第一條資料是(0,1)。
下面模拟一種異常狀況進行分析。
先建立表create unlogged table test03 (id int primary key, info text);
vi test.sql
setrandom id 1 100
insert into test03 values(:id, repeat(md5(random()::text), 1000)) on conflict on constraint test03_pkey do update set info=excluded.info;
第一個會話開啟長事務。
第二個會話運作:pgbench -m prepared -n -r -p 1 -f test.sql -c 48 -j 48 -t 2000
正常狀态時:
長時間運作第二個會話,關閉索引掃描出現的情況:
發現大部分都是heap塊。看到這裡知道了,這個其實是pg的hot update搞得鬼。
hot update 就是更新非索引字段時,會産生一個tuple2,索引指向ctid1,ctid1還是先指向原來的tuple1,再由tuple1指向ctid2,再由ctid2指向tuple2。如果你進行vacuum之後,就是索引指向ctid1,tuple1被回收,ctid1指向ctid2,ctid2指向tuple2。
現在用pageinspect看下這個表的索引情況:
這是有meta page,root page,branch page,leaf page這種結構的,root号是412。
從這兩張圖可以得出(1,1)到(99,1)除了(99,1)的第一條資料都是id=1的資料。下面看下一共有多少個。
是394個。
又重新查了下發現(99,1)這個裡面就沒有id=1的資料。一共掃描的塊數就是394+root page+leaf page=396,剛好和上面的計劃中掃描塊的總數相同。(這裡有點小疑問,我覺得應該是計劃的是對的,一共是有394條資料但是隻需要掃描393個塊,索引是掃描了3個,掃描了root leaf1 和leaf99,好像證明不了,按照計劃來推斷應該是這樣算的)。