在 PostgreSQL 中,单个数据页(Page)的最大存储行数取决于多种因素,包括行结构、数据类型、填充因子(Fill Factor)以及系统开销。以下是详细解析:
PostgreSQL 数据页默认大小为 8KB(8192 字节),其结构包括:
- 页头(Page Header):约 24 字节,存储页元信息(如页号、时间戳、状态位)。
- 行指针数组(ItemId Array):每个指针占 4 字节,记录每行数据的位置和状态。
- 空闲空间(Free Space):用于存储实际行数据。
- 行数据(Tuple Data):每行数据的实际内容。
每行数据包含:
- 行头(Tuple Header):23 字节(含事务 ID、锁信息等)。
- NULL 位图:若存在 NULL 字段,每 8 个字段占 1 字节。
- 对齐填充:按数据类型对齐(如 4 字节或 8 字节边界)。
不同数据类型占用空间不同:
- 定长类型:
INT(4 字节)、DATE(4 字节)、TIMESTAMP(8 字节)。
- 变长类型:
VARCHAR(n)、TEXT(实际长度 + 4 字节头部)。
- 默认值为 100%,表示页被填满后才会创建新页。
- 通过
CREATE TABLE 或 ALTER TABLE 设置较低的填充因子(如 90%),可为后续更新预留空间,减少页分裂。
- 对于超过页大小 1/4(约 2KB)的大字段,PostgreSQL 会将其存储到 TOAST 表,原行仅保留指针。
CREATE TABLE test_fixed (
id INT,
- 行大小:23(行头) + 4(NULL 位图) + 4(id) + 4(age) + 8(created_at) = 43 字节。
- 可用空间:8192(页大小) - 24(页头) - 4×N(行指针) ≈ 8168 字节(N 为行数,此处简化计算)。
- 理论最大行数:8168 ÷ 43 ≈ 189 行。
- 实际行数:因对齐和行指针开销,约 180-185 行。
CREATE TABLE test_variable (
id INT,
- 若
name 平均 20 字节,description 触发 TOAST(仅存指针 4 字节):
- 行大小:23(行头) + 4(NULL 位图) + 4(id) + 20(name) + 4(TOAST 指针) = 55 字节。
- 实际行数:8168 ÷ 55 ≈ 148 行。
SELECT reltuples, relpages, reltuples/relpages AS rows_per_page
FROM pg_class WHERE relname = 'your_table';
reltuples:表估计行数。
relpages:表占用页数。
- 避免过度宽表:减少字段数量,分离不常用字段。
- 使用 TOAST 存储大字段:对
TEXT、BYTEA 等类型自动优化。
- 调整填充因子:对更新频繁的表设置填充因子(如 90%)。
- 合理选择数据类型:避免使用过大的数据类型(如用
SMALLINT 替代 INT)。
PostgreSQL 单个数据页的行数上限取决于行大小和页结构开销,通常为 数十到数百行。实际应用中,建议通过 EXPLAIN 和统计信息分析查询性能,而非单纯追求高行数密度。优化数据模型和查询模式对提升性能更为关键。