PostgreSQL中page页结构

在PG中,磁盘存储和内存中的最小管理单位都是page,也是通常所说的block。一般PG页的大小为8K,在源码编译时可以设置。此后都不可更改,因为许多PG内存结构设计都是以此为基础的。

在一个page中,表的记录是从page的底部开始存储,然后慢慢向上涨。Page结构图如下:

上图为一个page的结构,主要由5个部分组成:

    Page Header:为页头,主要存储LSN,page中空闲空间的开始offset和结束offset等。下面再展开讲。

    ItemId data:是page中表记录的索引条目。一个索引条目4个字节,由两部分组成:此记录在page中的offset和记录长度length。

    Free space:是此page中剩余可用的空间,不算标记为delete后的空间;是指完全没有被使用的空间,也相当于page中没有被分配的空间。

    Item:就是指表实际存储的记录。

    Special space: 存储索引访问方法(AM: Access Method)信息,不同的索引访问方法,内容不一样。但如果是表的page,那么这里是空的,没有任何信息。

源码在src/backend/storage/page/bufpage.c中,以下为Page的初始化:

Page header 24个字节说明如下:

PageHeader 源码定义如下:

/*
 * disk page organization
 *
 * space management information generic to any page
 *
 *		pd_lsn		- identifies xlog record for last change to this page.
 *		pd_checksum - page checksum, if set.
 *		pd_flags	- flag bits.
 *		pd_lower	- offset to start of free space.
 *		pd_upper	- offset to end of free space.
 *		pd_special	- offset to start of special space.
 *		pd_pagesize_version - size in bytes and page layout version number.
 *		pd_prune_xid - oldest XID among potentially prunable tuples on page.
 *
 * The LSN is used by the buffer manager to enforce the basic rule of WAL:
 * "thou shalt write xlog before data".  A dirty buffer cannot be dumped
 * to disk until xlog has been flushed at least as far as the page's LSN.
 *
 * pd_checksum stores the page checksum, if it has been set for this page;
 * zero is a valid value for a checksum. If a checksum is not in use then
 * we leave the field unset. This will typically mean the field is zero
 * though non-zero values may also be present if databases have been
 * pg_upgraded from releases prior to 9.3, when the same byte offset was
 * used to store the current timelineid when the page was last updated.
 * Note that there is no indication on a page as to whether the checksum
 * is valid or not, a deliberate design choice which avoids the problem
 * of relying on the page contents to decide whether to verify it. Hence
 * there are no flag bits relating to checksums.
 *
 * pd_prune_xid is a hint field that helps determine whether pruning will be
 * useful.  It is currently unused in index pages.
 *
 * The page version number and page size are packed together into a single
 * uint16 field.  This is for historical reasons: before PostgreSQL 7.3,
 * there was no concept of a page version number, and doing it this way
 * lets us pretend that pre-7.3 databases have page version number zero.
 * We constrain page sizes to be multiples of 256, leaving the low eight
 * bits available for a version number.
 *
 * Minimum possible page size is perhaps 64B to fit page header, opaque space
 * and a minimal tuple; of course, in reality you want it much bigger, so
 * the constraint on pagesize mod 256 is not an important restriction.
 * On the high end, we can only support pages up to 32KB because lp_off/lp_len
 * are 15 bits.
 */

typedef struct PageHeaderData
{
	/* XXX LSN is member of *any* block, not only page-organized ones */
	PageXLogRecPtr pd_lsn;		/* LSN: next byte after last byte of xlog
								 * record for last change to this page */
	uint16		pd_checksum;	/* checksum */
	uint16		pd_flags;		/* flag bits, see below */
	LocationIndex pd_lower;		/* offset to start of free space */
	LocationIndex pd_upper;		/* offset to end of free space */
	LocationIndex pd_special;	/* offset to start of special space */
	uint16		pd_pagesize_version;
	TransactionId pd_prune_xid; /* oldest prunable XID, or zero if none */
	ItemIdData	pd_linp[FLEXIBLE_ARRAY_MEMBER]; /* line pointer array */
} PageHeaderData;

typedef PageHeaderData *PageHeader;

其中,PageXLogRecPtr为一个结构体,64位。记录xlog信息的原因:

  1. 保证buffer manger WAL原则,即写日志先于写数据

  2. 脏块checkpoint时,日志先刷出到disk

/*
 * For historical reasons, the 64-bit LSN value is stored as two 32-bit
 * values.
 */
typedef struct
{
	uint32		xlogid;			/* high bits */
	uint32		xrecoff;		/* low bits */
} PageXLogRecPtr;

总的来讲,PG中页的结构大体上与Oracle的Block结构是比较类似的,都是采用向上涨的方式来存储记录。但是在小细节上还是分别比较大的。Oracle的Block中还有ITL等事务相关信息等。

### PostgreSQL 存储结构详解 #### 数据库物理存储层次 PostgreSQL 的物理存储分为四个主要层次:表空间(Tablespaces),数据库(Databases),关系(Relations) 和 表页(Pages)[^1]。 - **表空间 (Tablespaces)** 是操作系统上的目录,用于存储特定于该表空间的数据文件。通过创建不同的表空间,管理员可以在不同磁盘位置上分布负载并管理数据放置策略。 - **数据库 (Databases)** 每个数据库由一组持久化对象组成,这些对象被保存在一个或多个表空间内。每个实例可以拥有多个独立的数据库,它们之间相互隔离。 - **关系 (Relations)** 关系是指逻辑上的表格、索引或其他持久化的集合体,在内部表示为一系列固定大小的块(称为 pages)。每种类型的 relation 都有自己的命名约定和访问方法。 - **表页 (Pages)** 页面是 PostgreSQL 文件系统的最小单位,默认情况下大小为8KB。每一个 page 可能包含多条记录,并且具有自己的头信息来描述其状态和其他属性[^2]。 #### WAL 日志机制 为了确保数据的一致性和可靠性,PostgreSQL 使用 Write-Ahead Logging(WAL) 技术。WAL 记录所有的更改操作之前先写入到日志中,即使发生崩溃也能依据此日志重做未完成的操作以保持一致性[^4]。 ```sql -- 查看当前wal_level设置 SHOW wal_level; ``` #### 缓冲区(Buffer) Buffer Manager 负责管理和维护内存中的缓存池,其中包含了最近使用的页面副本。当查询执行时,如果所需的数据已经在缓冲区内,则可以直接从中读取;否则就需要从磁盘加载相应的页面进入缓冲区[^3]。 #### 系统表与元数据 除了用户定义的关系外,还有许多系统表用来存储关于整个集群的信息,比如 `pg_class` 描述了所有类(即表和索引), `pg_attribute` 列出了所有列及其特性等。这类特殊表对于理解数据库架构至关重要。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值