开个新坑,简单写一写PostgreSQL中的元组管理。

对于整个存储架构,各种PostgreSQL的相关书籍已经介绍的很详细了,这里也就不再重复造轮子了。下面我们主要基于面向对象(大雾),来看看元组这个对象,有哪些成员变量,又有哪些成员函数


元组(Tuple)是PostgreSQL中数据的基本存储单元,Page又是存储元组的基本载体,所以这第一篇我们先来看一下Page的结构

Page的结构

Page的结构可以简单看下图所示

img

page的默认大小为8k。每个页面的起始位置有大小为24个字节的PageHeader,记录该page相关的元数据信息。PageHeader中的pd_lower和pd_upper分别指向了页面空闲空间的首尾。

这里每一个tuple存储一条数据记录,从数据页底部开始向前依次存储。这些元组在页面中的位置存储在行指针line pointer中,每个行指针指向一个tuple。行指针从前向后依次存储,形成一个简单的数据。行指针中还存放了元组的状态和大小信息,扮演元组在页面中的索引的角色。行指针和tuple中间的部分为页面的空闲空间。

我们首先来看看PageHeader存储了什么元数据

1
2
3
4
5
6
7
8
9
10
11
12
typedef struct PageHeaderData
{
PageXLogRecPtr pd_lsn;
uint16 pd_checksum;
uint16 pd_flags;
LocationIndex pd_lower;
LocationIndex pd_upper;
LocationIndex pd_special;
uint16 pd_pagesize_version;
TransactionId pd_prune_xid;
ItemIdData pd_linp[FLEXIBLE_ARRAY_MEMBER];
} PageHeaderData;
  • pd_lsn,记录了该页面最后一次更改的WAL log的lsn

  • pg_checksum,页面校验和

  • pd_flags,页面的标记为,包括以下标记

    • PD_HAS_FREE_LINES,页面中是否有unused line pointer. 这个unused的概念我们之后在看到line pointer时会介绍
    • PD_PAGE_FULL,页面是否有足够的空闲空间给新的tuple
    • PD_ALL_VISIBLE,页面中的所有元组是否对当前和之后的所有事务可见
  • pd_lower,指向空闲空间的起始位置

  • pd_upper,指向空闲空间的结束位置

  • pd_special,指向特殊空间的起始位置

  • pd_pagesize_version,页面布局的版本号

  • pd_prune_xid,用于页面元组清理时的标记位,记录了最旧的可清理的事务号。没有则为0

  • pg_linp,页面行指针数组

页面的行指针结构

行指针是一个32位大小的索引结构,如下:

1
2
3
4
5
6
typedef struct ItemIdData
{
unsigned lp_off:15,
lp_flags:2,
lp_len:15;
} ItemIdData;
  • lp_off,记录了tuple在页面中的offset

  • lp_len,记录了tuple的size

  • lp_flags,是一个tuple状态的简单标记,方便在扫描元组时做一个初步筛选,有以下几个标记

    • LP_UNUSED,元组未使用,可以立刻被重用
    • LP_NORMAL,元组状态正常
    • LP_REDIRECT,元组被重定向了。用于PG的HOT链的头部和中间元组
    • LP_DEAD,元组已死,但空间可能还没有回收