外观
PostgreSQL 物理架构
PostgreSQL的物理架构是指数据库在磁盘上的存储结构和文件组织方式。理解PostgreSQL的物理架构对于DBA进行存储管理、性能优化和故障恢复至关重要。
物理架构概述
PostgreSQL的物理存储结构由多个层次组成,从底层到上层依次为:
- 操作系统层:文件系统、磁盘I/O、内存管理
- PostgreSQL存储层:数据文件、WAL文件、配置文件等
- 数据库对象层:数据库、表、索引、视图等
PostgreSQL的物理存储采用目录树结构,所有数据和配置文件都存储在一个集中的目录中,称为数据目录(Data Directory)。
数据目录结构
数据目录是PostgreSQL存储所有数据和配置文件的根目录,由initdb命令创建。数据目录的结构如下:
data_directory/
├── PG_VERSION # PostgreSQL版本号
├── base/ # 数据库数据文件目录
├── global/ # 全局系统表目录
├── pg_commit_ts/ # 事务提交时间戳目录
├── pg_dynshmem/ # 动态共享内存目录
├── pg_hba.conf # 主机认证配置文件
├── pg_ident.conf # 身份映射配置文件
├── pg_logical/ # 逻辑复制目录
├── pg_multixact/ # 多事务ID目录
├── pg_notify/ # LISTEN/NOTIFY消息目录
├── pg_replslot/ # 复制槽目录
├── pg_serial/ # 序列生成器状态目录
├── pg_snapshots/ # 快照目录
├── pg_stat/ # 统计信息临时目录
├── pg_stat_tmp/ # 临时统计信息目录
├── pg_subtrans/ # 子事务状态目录
├── pg_tblspc/ # 表空间链接目录
├── pg_twophase/ # 两阶段事务目录
├── PG_VERSION # PostgreSQL版本号文件
├── pg_wal/ # WAL文件目录(旧版本为pg_xlog)
├── pg_xact/ # 事务ID目录
├── postgresql.auto.conf # 自动生成的配置文件
├── postgresql.conf # 主配置文件
└── postmaster.pid # 主进程PID文件主要目录和文件说明
PG_VERSION
- 包含PostgreSQL的主版本号(如"14"、"15")
- 用于验证数据目录与PostgreSQL二进制文件的兼容性
base/ 目录
- 存储各个数据库的数据文件
- 每个数据库对应一个子目录,目录名是数据库的OID
- 系统数据库postgres、template0、template1也存储在此目录
global/ 目录
- 存储全局系统表,如pg_database、pg_authid等
- 这些表不属于任何特定数据库,对所有数据库可见
pg_wal/ 目录
- 存储Write-Ahead Log(WAL)文件
- 用于数据恢复、复制和PITR(时间点恢复)
- WAL文件默认大小为16MB(可配置)
- WAL文件按顺序编号,形成WAL序列
pg_tblspc/ 目录
- 存储表空间的符号链接
- 每个符号链接指向实际的表空间目录
- 用于支持将表存储在不同的磁盘或文件系统
postgresql.conf
- PostgreSQL的主配置文件
- 包含数据库服务器的各种配置参数
- 支持动态加载部分参数(无需重启)
pg_hba.conf
- 主机基础认证配置文件
- 控制哪些主机可以连接到数据库
- 定义认证方法和访问权限
数据库与表的物理存储
数据库的物理存储
- 每个数据库在base/目录下有一个对应的子目录,目录名是数据库的OID
- 数据库的OID可以通过查询
pg_database系统表获取:sqlSELECT oid, datname FROM pg_database;
表的物理存储
- 每个表的数据存储在对应数据库目录下的一个或多个文件中
- 文件名格式为
relfilenode,其中relfilenode是表的物理文件标识符 - 可以通过查询
pg_class系统表获取表的relfilenode:sqlSELECT relname, relfilenode FROM pg_class WHERE relname = 'table_name';
大表的存储
- 当表的大小超过1GB时,PostgreSQL会创建多个文件
- 文件名格式为
relfilenode.1、relfilenode.2等 - 每个文件的最大大小由编译时参数
BLCKSZ和RELSEG_SIZE决定 - 默认情况下,每个文件包含131072个数据页(8KB/页 × 131072 = 1GB)
索引的物理存储
- 索引的物理存储方式与表类似
- 每个索引对应一个或多个文件,存储在对应数据库目录下
- 索引的relfilenode也存储在
pg_class系统表中
数据页结构
数据页是PostgreSQL存储数据的基本单位,默认大小为8KB(可在编译时调整)。数据页的结构如下:
+----------------+---------------------------------+----------------+------------+
| Page Header | Item Pointer Array | Free Space | Item Data |
| (24 bytes) | (variable size) | (variable) | (variable) |
+----------------+---------------------------------+----------------+------------+
| Page Footer (8 bytes) |
+---------------------------------------------------------------------+数据页各部分说明
Page Header(页头)
- 固定大小24字节
- 包含页的元数据:
pd_lsn:最后修改该页的WAL记录位置pd_checksum:页校验和(PostgreSQL 9.3+)pd_flags:页标志(如脏页标志)pd_lower:指向空闲空间开始的偏移量pd_upper:指向空闲空间结束的偏移量pd_special:指向特殊空间开始的偏移量(用于索引)pd_pagesize_version:页大小和版本信息
Item Pointer Array(行指针数组)
- 可变大小,每个行指针占用4字节
- 存储指向行数据的偏移量
- 行指针按逻辑顺序排列,但对应的行数据可能无序
- 行指针数组从页头之后开始,向上增长
Free Space(空闲空间)
- 页中未使用的空间
- 用于存储新插入的行数据
- 空闲空间位于行指针数组和行数据之间
Item Data(行数据)
- 存储实际的行记录
- 行数据从页的末尾开始,向下增长
- 每行数据包含元数据和用户数据
Page Footer(页尾)
- 固定大小8字节
- 包含:
- 页校验和(与页头的校验和相同)
- 页的LSN(与页头的LSN相同)
行数据结构
行数据存储在数据页的Item Data区域,每个行数据包含元数据和用户数据两部分:
行元数据
- xmin:创建该行的事务ID
- xmax:删除或更新该行的事务ID
- cid:命令ID(同一事务中多条命令的顺序)
- ctid:行的物理位置(页号:行号)
- infomask:行标志位(如是否有NULL值、是否有变长字段等)
- infomask2:更多行标志位(如是否有TOAST数据等)
- hoff:行头长度
用户数据
- 存储表中各列的实际数据
- 列数据按创建表时的顺序存储
- 支持NULL值压缩存储
- 支持变长字段(如text、varchar)
TOAST机制
TOAST(The Oversized-Attribute Storage Technique)是PostgreSQL用于存储大字段的机制:
- 当行数据大小超过页大小的25%时,大字段会被压缩或存储到TOAST表中
- TOAST表是一种特殊的辅助表,用于存储大字段数据
- TOAST机制支持四种存储策略:
- PLAIN:不使用TOAST,大字段直接存储
- EXTENDED:先压缩,再存储到TOAST表(默认)
- EXTERNAL:不压缩,直接存储到TOAST表
- MAIN:只压缩,不存储到TOAST表
WAL(Write-Ahead Logging)机制
WAL是PostgreSQL实现数据持久性和一致性的核心机制,确保在系统崩溃时能够恢复数据。
WAL的工作原理
- 所有数据修改操作先写入WAL日志
- WAL日志写入磁盘并刷新后,事务才能提交
- 后台进程(bgwriter、checkpointer)将脏页从共享缓冲区写入数据文件
- 系统崩溃时,通过重放WAL日志恢复未写入数据文件的修改
WAL文件结构
- WAL文件存储在pg_wal/目录中
- 文件名格式:
000000010000000000000001(时间线+逻辑日志序列号) - 默认大小为16MB(可通过
--wal-segsize参数在initdb时配置) - WAL文件按顺序生成,形成一个无限的WAL序列
WAL级联
- 当WAL文件填满时,PostgreSQL会创建新的WAL文件
- 旧的WAL文件会被归档(如果启用了归档)或回收(用于复制)
- WAL文件的回收和归档由WAL保留策略控制
WAL配置参数
wal_level:控制WAL的详细程度(minimal、replica、logical)synchronous_commit:控制事务提交时WAL的刷新策略wal_buffers:WAL缓冲区大小checkpoint_timeout:检查点间隔时间max_wal_size:WAL文件的最大保留数量min_wal_size:WAL文件的最小保留数量
表空间
表空间是PostgreSQL用于将数据库对象存储在不同磁盘或文件系统的机制,提供了灵活的存储管理能力。
表空间的物理存储
- 表空间在物理上对应一个目录
- 表空间目录可以位于任何文件系统
- pg_tblspc/目录中包含指向实际表空间目录的符号链接
- 符号链接的名称是表空间的OID
表空间的创建和管理
- 使用
CREATE TABLESPACE命令创建表空间 - 使用
ALTER TABLESPACE命令修改表空间 - 使用
DROP TABLESPACE命令删除表空间 - 使用
TABLESPACE子句指定表或索引的表空间
表空间的应用场景
- 将热点数据存储在高性能存储(如SSD)
- 将冷数据存储在低成本存储(如HDD)
- 将索引和数据分开存储,提高I/O并行度
- 管理不同磁盘的空间使用
- 实现存储的分级管理
存储管理
空间分配与回收
PostgreSQL使用多种机制管理存储空间:
- 自动空间分配:当表需要更多空间时,自动创建新的数据文件
- VACUUM操作:回收死元组占用的空间
- autovacuum进程:自动执行VACUUM和ANALYZE操作
- TOAST机制:高效存储大字段数据
- 表分区:将大表分割为多个小表,便于管理
存储监控
DBA需要定期监控PostgreSQL的存储使用情况:
表大小监控:
sqlSELECT pg_size_pretty(pg_total_relation_size('table_name'));数据库大小监控:
sqlSELECT pg_size_pretty(pg_database_size('database_name'));WAL使用监控:
sqlSELECT pg_wal_lsn_diff(pg_current_wal_lsn(), pg_last_wal_replay_lsn()) AS replication_lag;表空间使用监控:
sqlSELECT spcname, pg_size_pretty(pg_tablespace_size(spcname)) AS size FROM pg_tablespace;
存储优化
选择合适的文件系统:
- 推荐使用ext4、XFS或ZFS
- 避免使用NTFS(Windows环境)
- 考虑使用SSD提高性能
优化磁盘I/O:
- 将WAL文件和数据文件存储在不同的磁盘
- 使用RAID技术提高可靠性和性能
- 调整文件系统参数(如noatime、nodiratime)
调整PostgreSQL参数:
shared_buffers:调整共享缓冲区大小work_mem:调整排序和哈希操作的内存maintenance_work_mem:调整维护操作的内存effective_io_concurrency:调整异步I/O的并发度
合理规划表空间:
- 将热点表和索引放在高性能存储
- 将历史数据和冷数据放在低成本存储
- 定期清理无用的数据
故障恢复机制
PostgreSQL的物理架构设计支持多种故障恢复机制:
崩溃恢复
- 当PostgreSQL异常关闭时,会自动执行崩溃恢复
- 恢复过程包括:
- 重新启动数据库服务器
- 读取并验证WAL文件
- 重放未提交的WAL记录
- 回滚未完成的事务
- 恢复数据库到一致状态
时间点恢复(PITR)
- 基于基础备份和WAL归档实现
- 允许将数据库恢复到任意时间点
- 恢复过程包括:
- 还原基础备份
- 重放WAL归档文件
- 停止在指定的时间点或事务ID
基于WAL的复制
- 主库将WAL记录发送到从库
- 从库重放WAL记录,保持与主库数据一致
- 支持同步复制和异步复制
- 用于实现高可用性和负载均衡
备份与恢复的物理层面
物理备份
- 直接复制数据目录或表空间
- 优点:备份和恢复速度快,适用于大型数据库
- 缺点:不支持跨版本恢复,不能选择性恢复
- 工具:pg_basebackup、rsync等
逻辑备份
- 导出数据库对象的逻辑结构和数据
- 优点:支持跨版本恢复,可选择性恢复
- 缺点:备份和恢复速度慢,适用于小型数据库
- 工具:pg_dump、pg_dumpall、pg_restore等
增量备份
- 基于WAL归档实现
- 只备份自上次备份以来的变化
- 优点:备份速度快,占用空间小
- 缺点:恢复过程复杂,需要基础备份
案例分析:存储性能优化
背景:某电商平台在促销期间,数据库I/O负载过高,导致系统响应缓慢。
分析:
- 监控显示磁盘I/O使用率接近100%
- 主要瓶颈是WAL写入和数据文件更新
- 数据库和WAL文件存储在同一磁盘上
- 使用的是普通HDD磁盘,IOPS不足
优化措施:
存储分离:
- 将WAL文件迁移到专用SSD磁盘
- 将热点表和索引迁移到SSD表空间
- 将历史数据迁移到HDD表空间
参数调整:
- 调整
shared_buffers从1GB增加到4GB - 调整
wal_buffers从16MB增加到64MB - 调整
checkpoint_timeout从5min增加到15min - 调整
max_wal_size从1GB增加到4GB
- 调整
文件系统优化:
- 将文件系统挂载选项改为
noatime,nodiratime - 调整XFS文件系统的日志级别
- 将文件系统挂载选项改为
结果:
- 磁盘I/O使用率从95%降低到30%
- 事务提交延迟从200ms降低到50ms
- 系统能够处理的并发请求数增加3倍
- 促销期间系统稳定运行,未出现性能问题
