PostgreSQL内幕剖析——结构与架构

大家好,这里是失踪人口bang__bang_,从今天开始持续更新PostgreSQL内幕相关内容,让我们一起了解学习吧✊!


目录

[1️⃣ DB集群、数据库、表](#1️⃣ DB集群、数据库、表)

[🍙 数据库集群的逻辑结构](#🍙 数据库集群的逻辑结构)

[🍙 数据库集群的物理结构](#🍙 数据库集群的物理结构)

[🍥 数据库集群布局](#🍥 数据库集群布局)

[🍥 数据库布局](#🍥 数据库布局)

[🍥 表和索引相关联文件的布局](#🍥 表和索引相关联文件的布局)

[🍥 表空间布局](#🍥 表空间布局)

[🍙 堆表文件的结构布局](#🍙 堆表文件的结构布局)

[🍙 元组读写方法](#🍙 元组读写方法)

[🍥 写入元组](#🍥 写入元组)

[🍥 读取元组](#🍥 读取元组)

[2️⃣ 进程架构和内存架构](#2️⃣ 进程架构和内存架构)

[🍙 进程架构](#🍙 进程架构)

[🍙 内存架构](#🍙 内存架构)


1️⃣ DB集群、数据库、表

🍙 数据库集群的逻辑结构

集群逻辑图

数据库是数据库对象的集合,数据库对象可以是数据库本身、表、索引、试图等。

数据库集群是PostgreSQL数据库服务器在单个主机上运行并管理单个数据库集群。

PostgreSQL中的所有数据库对象都由Oid(unsigned int)4字节的无符号整数标识

exp:

数据库的Oid存储在pg_database;堆表的Oid存储在pg_class中。

sql 复制代码
select datname,oid from pg_database where datname="simpledb";
select relname,oid from from pg_class where relname="simpletbl";

🍙 数据库集群的物理结构

物理结构图

数据库集群其实是一个base directory 目录,在初始化程序时创建基目录,其路径由系统变量PGDATA设置。

一切皆文件 ,在postgresql中,数据库是存储在base目录下的子目录文件,表和索引则是对应数据库目录下的文件。特殊的表空间也是一个目录,包含基本目录之外的数据,在根目录下**/tablespace**。

🍥 数据库集群布局

基目录下文件的布局:

files description
PG_VERSION 包含 PostgreSQL 主版本号的文件。
current_logfiles 记录日志记录收集器当前写入的日志文件的文件。
pg_hba.conf 用于控制 PostgreSQL 的客户端身份验证的文件。
pg_ident.conf 用于控制 PostgreSQL 的用户名映射的文件。
postgresql.conf 用于设置配置参数的文件。
postgresql.auto.conf 用于存储在 ALTER SYSTEM 中设置的配置参数的文件。(版本 9.4 或更高版本)
postmaster.opts 一个文件,记录上次启动服务器时使用的命令行选项。

基目录下子目录的布局:

subdirectories description
base/ 包含每个数据库的子目录的子目录。
global/ 包含集群范围的表(如 pg_database 和 pg_control)的子目录。
pg_commit_ts/ 包含事务提交时间戳数据的子目录。(版本 9.5 或更高版本)
pg_clog/ (versions 9.6 or earlier) 包含事务提交状态数据的子目录。在版本 10 中,它已重命名为 pg_xact
pg_dynshmem/ 包含动态共享内存子系统使用的文件的子目录。(版本 9.4 或更高版本)
pg_logical/ 包含用于逻辑解码的状态数据的子目录。(版本 9.4 或更高版本)
pg_multixact/ 包含多事务状态数据的子目录。(用于共享行锁)
pg_notify/ 包含 LISTEN/NOTIFY 状态数据的子目录。
pg_repslot/ 包含复制槽数据的子目录。(版本 9.4 或更高版本)
pg_serial/ 子目录,其中包含有关已提交的可序列化事务的信息。(版本 9.1 或更高版本)
pg_snapshots/ 包含导出快照的子目录。 PostgreSQL 的函数pg_export_snapshot在此子目录中创建快照信息文件。 (版本 9.2 或更高版本)
pg_stat/ 包含 statistics 子系统的永久文件的子目录。
pg_stat_tmp/ 包含 statistics 子系统的临时文件的子目录。
pg_subtrans/ 包含子事务状态数据的子目录。
pg_tblspc/ 包含指向表空间的符号链接的子目录。
pg_twophase/ 包含准备好的事务的状态文件的子目录。
pg_wal/ (versions 10 or later) 包含 WAL(预写日志记录)段文件的子目录。它是从版本 10 中的 pg_xlog 重命名而来的。
pg_xact/ (versions 10 or later) 包含事务提交状态数据的子目录。它是从版本 10 中的 pg_clog 重命名而来的。
pg_xlog/ (versions 9.6 or earlier) 包含 WAL(预写日志记录)段文件的子目录。在版本 10 中,它已重命名为 pg_wal

在版本 10 中,子目录 pg_xlog 和 pg_clog 分别重命名为 pg_wal 和 pg_xact。

🍥 数据库布局

物理存储上数据库目录的name和Oid相同,数据库路径如base/oid。

🍥 表和索引相关联文件的布局

每个大小小于1GB的表或索引会存储在其数据库目录下的单个文件中。表和索引由单个Oid在内部管理,他们的数据内容由变量relfilenode进行管理,Oid和relfilenode并不总是相同。

1.一般情况下,Oid和relfilenode对应相同,此时数据文件路径则为数据库目录下的relfilenode,如base/oid/relfilenode。

2.当使用TRUNCATE、REINDEX、CLUSTER等来更改表和索引的 relfilenode 值后会删除旧的数据文件,生成新的数据文件,此时Oid和relfilenode就不相同了。

内置函数pg_relation_filepath()可以返回具有指定oid或名称关系的文件路径。

如果当表或索引的数据大小大于1GB,则会创建一个以relfilenode.1为名的新文件,依次类推。

表文件会有两个后缀"_fsm"和"_vm"的文件分别是free space map和visibility map,表示空闲空间映射和可用空间映射,fsm和vm在内部可以称为每个关系的分叉,数据文件的分叉编号为0,fsm的分叉编号为1,vm的分叉编号为2。

🍥 表空间布局

表空间布局

使用CREATE TABLESPACE语句在指定目录下创建表空间,则会在该指定目录下创建一个特定于版本的子目录。 子目录命名规则为PG_目录名_版本号

sql 复制代码
CREATE TABLESPACE tablespace_name
    [ OWNER { new_owner | CURRENT_ROLE | CURRENT_USER | SESSION_USER } ]
    LOCATION 'directory'
    [ WITH ( tablespace_option = value [, ... ] ) ]

1.表空间由pg_tblspc子目录进行符号链接寻址,链接名称则为生成的tablespace的Oid值。

2.如果要在特定版本的表空间中创建一个数据库,则会在tablespace中生成一个子目录,目录名为数据库Oid。

3.如果要对基目录中的数据库创建一个新表,则首先会在特定版本的表空间中生成一个数据库目录,其目录名为基目录的Oid值,再在其下生成一个表文件。

🍙 堆表文件的结构布局

在PostgreSQL中数据文件是由一个个大小为8kb的页面(也称作块)组成的,当文件大小不够使用时,PostgreSQL会在文件末尾添加新的空页,页面号由0开始编号。
堆表文件结构

每个页面的内部结构如下:
页面内部结构

关键结构:

  • heap tuples(堆元组):tuple(堆元组)是自页面底部进行堆放的。

  • line pointers(行指针数组): line pointer是4字节长度的行指针,其中包含指向tuple的指针,行指针形成一个简单的数组,行指针按顺序编号,称为偏移量编号,当一个新的tuple被添加到页面时自然而然的推送一个新的行指针到数组上指向新的tuple。

  • **Header Info(标头数据):**页面开头分配长度为24字节。pd_lower和pd_upper所指向的中间空白区域称为free space.

    字段 描述
    pd_lsn 8字节,用于wal机制,LSN:此页的最后一次更改的 WAL 记录的最后一个字节之后的下一个字节
    pd_checksum 2字节,用于校验和
    pd_flags 2字节,标志位
    pd_lower 2字节,指向行指针末尾,描述空闲空间的开头
    pd_upper 2字节,指向最新堆元组开头,描述空闲空间的末尾
    pd_special 2字节,表示索引,在表空间中指向页面末尾,在索引空间中指向特殊空间开头
    pd_pagesize_version 2字节,页面大小和布局版本号信息
    pd_prune_xid 4字节,页面上最旧的未修剪 XMAX,如果没有则为零

使用TID (元组标识符)标识表中的元组,TID是由一对值构成**(页面号,指向目标元组的行指针偏移量)。**

此外,大小大于约 2 KB(约为 8 KB 的 1/4)的堆元组使用称为 TOAST(超大属性存储技术)的方法进行存储和管理。

🍙 元组读写方法

🍥 写入元组

写入元组示意图

阶段1:页面只有元组tupe1,pd_lower指向行指针末尾即line pointer1,line pointer1指向对应堆元组tuple1,pd_upper指向最新堆元组开头tuple1。

阶段2: 向页面写入新的元组tuple2,对应推送一个新的line pointer2到数组,其指向tuple2,pd_lower更新指向最新行指针末尾,pd_upper更新指向最新tuple2的开头,对应的其他字段也进行更新。

🍥 读取元组

顺序扫描:顺序扫描是按序遍历页面的行指针数组即遍历对应的堆元组。

B树索引扫描:B树索引扫描是使用了一个索引文件,索引文件中有索引元组,基本构成为(索引键,指向目标堆元组的TID),TID上述介绍为(页面号,行指针偏移量),通过扫描索引文件中的索引元组获取到对应目标索引键拿到TID,通过TID拿到数据。
读取元组示意图

2️⃣ 进程架构和内存架构

🍙 进程架构

PostgreSQL是一个多进程架构,具有一个所有进程的父进程Postgres进程、2个后端进程接受客户端的请求,7个后台进程作不同的功能、1个复制关联进程,v9.3后支持后台工作进程,他们构成了PostgreSQL的服务器。
进程架构图

postgres服务器进程是PostgreSQL服务器中所有进程的父进程,在启动pg_ctl程序时带上start选项,postgres服务器进程,他会创建一个共享内存、各种后台进程、根据需要启动复制关联进程,接收到客户端请求时创建一个后端进程。

PostgreSQL本机没有连接池功能,面对postgres服务器进程被大量请求,开销会很大,通常使用中间件来解决。

🍙 内存架构

PostgreSQL的内存架构分为两种:

  • 本地内存区域:每个后端进程backend process本地分配使用。

  • 共享内存区域:PostgreSQL服务器所有进程一起使用。

内存架构图

本地内存区域 作用
temp_buffers 执行器用来存储临时表
work_mem 使用order by/distinct操作对元组进行排序后使用hash-join或者merge-join连接表
maintenance_work_mem 某些类型的维护操作使用区域
共享内存区域 作用
shared buffer pool 将表和索引中的页面从持久存储加载到该区域并直接操作
WAL buffer WAL数据写入持久存储之前的缓冲区
CommitLog 提交日志保留并发控制的所有事务的状态

结语:本文开始剖析PostgreSQL的内幕,本篇主要介绍PostgreSQL中的集群、数据库、表是什么即他们的逻辑结构和实际的物理结构。同时介绍了堆表文件写入读取元组的方法,并简单讲解PostgreSQL的进程架构和内存架构。

参考文章:https://www.interdb.jp/pg/index.html

相关推荐
20242817李臻1 小时前
20242817李臻-安全文件传输系统-项目验收
数据库·安全
行思理1 小时前
MongoDB慢查询临时开启方法讲解
数据库·mongodb
bbsh20991 小时前
WebFuture 升级提示“不能同时包含聚集KEY和大字段””的处理办法
数据库·sql·mysql·webfuture
Zfox_6 小时前
Redis:Hash数据类型
服务器·数据库·redis·缓存·微服务·哈希算法
陈丹阳(滁州学院)8 小时前
若依添加添加监听容器配置(删除键,键过期)
数据库·oracle
远方16098 小时前
14-Oracle 23ai Vector Search 向量索引和混合索引-实操
数据库·ai·oracle
GUIQU.10 小时前
【Oracle】数据仓库
数据库·oracle
恰薯条的屑海鸥10 小时前
零基础在实践中学习网络安全-皮卡丘靶场(第十六期-SSRF模块)
数据库·学习·安全·web安全·渗透测试·网络安全学习
咖啡啡不加糖10 小时前
Redis大key产生、排查与优化实践
java·数据库·redis·后端·缓存
曼汐 .10 小时前
数据库管理与高可用-MySQL高可用
数据库·mysql