企业🤖AI Agent构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
       在数据文件内部(堆表和索引,以及空闲空间映射和可见性映射),它被分成固定长度的页(或块),默认为8192字节(8 KB)。 每个文件中的那些页从 0 开始按顺序编号,这样的编号称为块编号。 如果文件已被填满,PostgreSQL 会在文件末尾添加一个新的空页以增加文件大小。 ![](https://img.kancloud.cn/ef/2a/ef2add6c8a1ad37d366988eed5e326e0_3126x1088.png) 表中的页面包含三种数据: * heap tuples: 行数据 * line pointers: 行指针,指向对应的tuple,offffset number从1开始 * header data: * pd_lsn :保存此page的最新的WAL记录的lsn,用于数据恢复的 * pd_lower :指向最新的line pointer * pd_upper: 指向最新的tuple * pd_checksum:此变量存储此页面的校验和值。 * pd_special:此变量用于索引。 在表格内的页面中,它指向页面的末尾。 (在索引内页中,它指向特殊空间的开始,即只由索引保存的数据区,根据B-tree、GiST、GiN等索引类型包含特定数据。) **写操作**        假设一个表由一页组成,其中只包含一个堆元组。 本页的pd_lower指向第一行指针,行指针和pd_upper都指向第一个堆元组。 见图 (a)。        当插入第二个元组时,它被放置在第一个元组之后。 第二行指针被推到第一行指针上,它指向第二个元组。 pd_lower 更改为指向第二行指针,pd_upper 更改为指向第二个堆元组。 见图 (b)。 此页中的其他头数据(例如,pd_lsn、pg_checksum、pg_flag)也被重写为适当的值 ![](https://img.kancloud.cn/81/c5/81c5536af8656cb31a22ef7d8c5b011a_3054x682.png) **读操作** * sequential scan:遍历所有的pages的line pointers获取所有的tuples; * index scan:通过索引获取tids,然后再根据tids获取tuples; * index only scan:一般来说只扫描索引就能获取需要的字段,不需要回表读,需要VM配合使用; * bitmap index scan:当index scan命中大量的rows时,将会构造一个page的bitmap(一个bit对应一个 page),然后在recheck每个page找到符合条件的rows,这样可以避免重复扫描page; ![](https://img.kancloud.cn/05/c5/05c5ee3bc28a67f81ca7a015c7232fa5_2908x1842.png)