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。
bt_page_stats信息
btpo_flags=2是root节点,btpo=2不是最底层。
bt_page_items信息
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,好像证明不了,按照计划来推断应该是这样算的)。