PG第五章-体系结构

文章目录

  • 前言
  • 5.1. 逻辑和物理存储结构
    • 5.1.1.逻辑存储结构
    • 5.1.2.物理存储结构
      • 1. 数据目录结构
      • 2. 数据文件布局
        • ( 1) OID 对象标识符
        • ( 2) 表空间
        • ( 3) 数据文件命名
        • ( 4 )表文件内部结构
  • 5.2. 进程结构
    • 5.2.1.守护进程与服务进程
    • 5.2.2.辅助进程
  • 5.3. 内存结构
    • 5.3.1.本地内存
    • 5.3.2.共享内存
  • 5.4.本章小结


前言

数据库集簇指由单个 PostgreSQL 服务器实例管理的数据库集合,组成数据库集簇的这些数据库使用相同的全局配置文件和监听端口 、共用进程和内存结构。
某一个数据库实例通常是指某个数据库集簇

5.1. 逻辑和物理存储结构

5.1.1.逻辑存储结构

数据库集簇是数据库对象的集合。
数据库本身也是数据库对象 , 一个数据库集簇可以包含多个 Database 、多个 User ,每个 Database 以及 Database 中的所有对象都有它们的所有者 : User。
创 建一 个 Database 时 会 为这个 Databas巳创 建一 个 名为 public 的 默 认 Schema ,每个 Database 可以 有多个 Schema ,在这个数据库中创建其他数据库对象时如 果没有指定Schema , 都会在 public 这个 Schema 中。Schema 可 以 理解为一个数据库 中的命名空 间 ,在数据库 中创建的所有对象都在 Schema 中创建 一个用户可以从 同一个客户端连接中访问不 同的 Schema。 不同的 Schema 中可以有多个相同名称的 Table 、 Index 、 View 、 S equence、Function 等数据库对象 。

5.1.2.物理存储结构

数据库的文件默认保存在 initdb 时创建的数据目 录 中,数据文件之外 还有参数文件 、控制文件、数据库运行 日 志及预写 日志等 。

1. 数据目录结构

数据目录用来存放 PostgreSQL 持久化的数据 , 通常可以将数据目录路径配置为 PGDATA环境变量,查看数据目 录有哪些子目录和文件的命令如下所示 :

[postgres@localhost ~]$ tree -L 1 $PGDATA
/usr/local/pgsql/data
├── base
├── global
├── pg_commit_ts
├── pg_dynshmem
├── pg_hba.conf
├── pg_ident.conf
├── pg_logical
├── pg_multixact
├── pg_notify
├── pg_replslot
├── pg_serial
├── pg_snapshots
├── pg_stat
├── pg_stat_tmp
├── pg_subtrans
├── pg_tblspc
├── pg_twophase
├── PG_VERSION
├── pg_wal
├── pg_xact
├── postgresql.auto.conf
├── postgresql.conf
├── postmaster.opts
└── postmaster.pid

PG第五章-体系结构_第1张图片

2. 数据文件布局

base 子目录是数据文件默认保存的位置,是数据库初始化后的默认表空间
基础的数据库对象 : OID 和表空间

( 1) OID 对象标识符

所有数据库对象都由各自的对象标识符( OID )进行内部管理,它们是
无符号的 4 字节整数 。 数据库对象和各个 OID 之间的关系存储在适当的系统目录中,具体取决于对象的类型 。 数据库的 OID 存储在 pg_database 系统表中,可以通过如下代码查询数据库的 OID:

postgres=# select oid,datname from pg_database where datname='postgres';
  oid  | datname  
-------+----------
 13892 | postgres
(1 row)

数据库 中的表、索引 、序列等对象的 OID 存储在 pg_class 系统表中,可以通过如下代码查询获得这些对象的 OID:

postgres=# select oid,relname,relkind from pg_class where relname='tb1_user_json';
  oid  |    relname    | relkind 
-------+---------------+---------
 24594 | tb1_user_json | r

( 2) 表空间

最大的逻辑存储单位是表空间,数据库中创建的对象都保存在表空间
中 ,例如表、索引和整个数据库都可以被分配到特定的表空间 。 在创建数据库对象时,可以指定数据库对象的表空间,如果不指定则使用默认表空间,也就是数据库对象的文件的位置。 初始化数据库目录时会自动创建 pg_default 和 pg_global 两个表空间 。

postgres=# \db
       List of tablespaces
    Name    |  Owner   | Location 
------------+----------+----------
 pg_default | postgres | 
 pg_global  | postgres | 
(2 rows)

  • pg_global 表空间的物理文件位置在数据目录的 global 目录中,它用来保存系统表 。
  • pg_default 表空间的物理文件位 置在数据目录中的 base 目录,是 template0 和template1 数据库的默认表空间,创建数据库时,默认从 template1数据库进行克隆,因此除非特别指定了新建数据库的表空 间 ,默认使用template1的表空间 , 也就是 pg_default。

除 了两个默认表空 间 , 用户还可以创建自定义表空间 。 使用自定义表空 间有两个典型的场景 :

  • 通过创建表空间解决已有表空间磁盘不足并无法逻辑扩展的 问题 ;
  • 将索引、 WAL 、数据文件分配在性能不同的磁盘上,使硬件利用率和性能最大化 。

由于现在固态存储已经很普遍,这种文件布局方式反倒会增加维护成本 。
要创建一个表空间,先用操作系统的 postgres 用户创建一个目录,然后连接到数据库,使用 CREATE TABLESPACE 命令创建表空间,如下所示:

[postgres@localhost data]$ mkdir -p /usr/local/pgsql/data/mytblspc
[postgres@localhost data]$ psql
psql (14.5)
Type "help" for help.

postgres=# create tablespace myspc location '/usr/local/pgsql/data/mytblspc';
WARNING:  tablespace location should not be inside the data directory
CREATE TABLESPACE
postgres=# \db
                  List of tablespaces
    Name    |  Owner   |            Location            
------------+----------+--------------------------------
 myspc      | postgres | /usr/local/pgsql/data/mytblspc
 pg_default | postgres | 
 pg_global  | postgres | 
(3 rows)

创建新的数据库或表时,可以指定创建的表空 间

create table t(id serial primary key,ival int) tablespace myspc;

由于表空间定义了存储的位置 ,在创建数据库对象时,会在当前的表空 间 目录创建一个以数据库 OID 命名的目录,该数据库的所有对象将保存在这个目录中,除非单独指定表空 间 。

postgres=# select oid,datname from pg_database where datname='postgres';
  oid  | datname  
-------+----------
 13892 | postgres
(1 row)
[postgres@localhost data]$ ll $PGDATA/base/13892
total 1723404
-rw-------. 1 postgres postgres      8192 Oct 21 22:35 112
-rw-------. 1 postgres postgres      8192 Oct 21 22:35 113
-rw-------. 1 postgres postgres    122880 Oct 24 20:18 1247
-rw-------. 1 postgres postgres     24576 Oct 21 23:32 1247_fsm
[postgres@localhost data]$ ll $PGDATA/mytblspc/PG_14_202107181/13892
total 0
-rw-------. 1 postgres postgres 0 Oct 24 20:16 24639

通过以上查询可知 postgres 的 OID 为13892 ,postgres 的表、索引都会保存
在 $PGDATA/base/13892 这个目录中

( 3) 数据文件命名

在数据库中创建对象,例如表、索引时首先会为表和索引分配段 。 在 PostgreSQL 中,每个表和索引都用一个文件存储,新创建的表文件以表的 OID 命名 , 对于大小超出 l GB 的表数据文件, PostgreSQL 会自动将其切分为多个文件来存储,切分出的文件用 OID.<顺序号〉来命名 。 但表文件并不是总是“ OID .< 顺序号 > ”命名 ,实际上真正管理表文件的是pg_class 表中的 relfilenode 字段的值,在新创建对象时会在 pg_class 系统表中插入该表的记录,默认会以 OID 作为 relfilenode 的值,但经过几次 VACUUM 、 TRUNCATE 操作之后,relfilenode 的值会发生变化 。

postgres=# select oid,relfilenode from pg_class where relname='t';
  oid  | relfilenode 
-------+-------------
 24639 |       24639
(1 row)

postgres=# \! ls -l /usr/local/pgsql/data/base/13892/24639*
-rw-------. 1 postgres postgres      8192 Oct 23 00:31 /usr/local/pgsql/data/base/13892/24639 

默认情况下 , tbl 表的 OID 为 24639 , relfilenode 也是 24639 ,表 的物理文件为“/usr/local/pgsql/data/base/13892/24639 ” 。 依次 TRUNCATE 清空 t 表的所有数据

postgres=# create table test(id int4 primary key);
CREATE TABLE
postgres=# select oid,relfilenode from pg_class where relname='test';
  oid  | relfilenode 
-------+-------------
 24647 |       24647
(1 row)

postgres=# \! ls -l /usr/local/pgsql/data/base/13892/24647*
-rw-------. 1 postgres postgres 0 Oct 24 21:19 /usr/local/pgsql/data/base/13892/24647
postgres=# truncate table test;
TRUNCATE TABLE
postgres=# checkpoint;                                               
CHECKPOINT
postgres=# \! ls -l /usr/local/pgsql/data/base/13892/24647*
ls: cannot access /usr/local/pgsql/data/base/13892/24647*: No such file or directory
postgres=# select oid,relfilenode from pg_class where relname='test';
  oid  | relfilenode 
-------+-------------
 24647 |       24652
(1 row)

postgres=# \! ls -l /usr/local/pgsql/data/base/13892/24652*
-rw-------. 1 postgres postgres 0 Oct 24 21:20 /usr/local/pgsql/data/base/13892/24652

在 默认情况下 表的物理文件为“ /usr/local/pgsql/data/base/13892/24647 ” 。 依次 TRUNCATE 清空表的所有数据后它的命名规则为 <relfilenode >.<顺序号>/usr/local/pgsql/data/base/13892/24652

CREATE TABLE tbl(ival int4,description text,create_time timestamp(6));
insert into tbl(ival,description,create_time) select (random()*(2*10^9))::integer as ival,substr('abcdffddddddd',1,(random()*26)::integer)
 as description,date(generate_series(now(),now()+'1 week','1 day')) as create_time from generate_series(1,2000000);
postgres=#  select pg_size_pretty(pg_relation_size('tbl'::regclass));
 pg_size_pretty 
----------------
 850 MB
(1 row)

通过上述命令看到 tbl 表的大小目前为 850MB ,执行一些 UPDATE 操作后再次查看数据文件
postgres=# ! ls -lh /usr/local/pgsql/data/base/13892/24654*
-rw-------. 1 postgres postgres 1.0G Oct 24 22:01 /usr/local/pgsql/data/base/13892/24654
-rw-------. 1 postgres postgres 677M Oct 24 22:02 /usr/local/pgsql/data/base/13892/24654.1
-rw-------. 1 postgres postgres 448K Oct 24 22:02 /usr/local/pgsql/data/base/13892/24654_fsm
-rw-------. 1 postgres postgres 32K Oct 24 22:01 /usr/local/pgsql/data/base/13892/24654_vm
如前文所述,数据文件的命名规则为 <relfilenode> .<顺序号>, tbl 表的大小超过 lGB,tbl 表的 relfilenode 为 24591 ,超出 lGB 之外的 数据会按每 GB 切割 , 在文件系统中查看时就是名称为 24654.1 的数据文件 。 在上述输出结果中,后缀为_fsm 和_vm 的这两个表文件的附属文件是空 闲空 间映射表文件和可见性映射表文件 。 空 闲空间映射用来映射表文件中可用的空 间 ,可见性映射表文件跟踪哪些页面只包含己知对所有活动事务可见的元组,它
也跟踪哪些页面只包含未被冻结的元组 。 图 5 -2 显示了 PostgreSQL 数据目录、表空间以及文件的结构概貌 。
PG第五章-体系结构_第2张图片

( 4 )表文件内部结构

将保存在磁盘中的块称为 Page ,而将内存中的块称为 Buffer ,表和
索引称为 Relation ,行称为 Tuple。 数据的读写是以 Page 为最小单位, 每个Page 默认大小为 8kB ,在编译 时指定的 BLOCKSZ 大小决定 Page 的大小 。 每个表文件由多个 BLOCKSZ 字节大小的 Page 组成, 每个 Page 包含若干 Tuple 。 对于 I/O 性能较好的硬件,并且以分析为主的数据库,适当增加 BLCKSZ 大小可以小幅提升数据库性能 。
PG第五章-体系结构_第3张图片
PageHeader 描述了一个数据页的页头信息,包含页的一些元信息 。 它的结构及其结构指针 PageHeader 的定义如下:

  • pd_lsn : 在 ARIES Recovery Algorithm 的解释中,这个 lsn 称为 PageLSN ,它确定和记录了最后更改此页的 xlog 记录的 LSN ,把数据页和 WAL 日志关联,用于恢复数据时校验日志文件和数据文件的一致’性; pd_lsn 的高位为 xlogid ,低位记录偏移量 ;因为历史原因, 64 位的 LSN 保存为两个 32 位的值 。
  • pg_flags :标识页面的数据存储情况 。
  • pd_special : 指向索引相关数据的开始位置,该项在数据文件中为空,主要是针对不同索引 。
  • pd_lower :指向空闲空间的起始位置 。
  • pd_upper :指向空闲空间的结束位置 。
  • pd_pagesize_version : 不同的 PostgreSQL 版本的页的格式可能会不同 。
  • pd一linp[l]:行指针数组,即图 5-3 中的 Item!, Item2, … , Itemn ,这些地址指向Tuple 的存储位置 。

如果一个表由 一个只包含一个堆元组 的页面组成 。 该页面的 pd_lower 指向 第一行指针,并且行指针和 pd_upper 都指向第一个堆元组 。 当第二个元组被插入时,它被放置在第一个元组之后。第二行指针被压入第一行,并指向第二个元组 。 pd_lower 更改为指向第二行指针, pd_upper 更改为第二个堆元组 。 此页面中的其他头数据(例如, pd_lsn, pg_checksum 、 pg_flag )也被重写为适当的值 。
当从数据库中检索数据时有两种典型的访问方法,顺序扫描和 B 树索引扫描 。
顺序扫描通过扫描每个页面中的所有行指针顺序读取所有页面中的所有元组 。 B 树索引扫描时 ,索引文件包含索引 元组 , 每个元组由索引键和指 向目 标堆元组的 TID 组成 。 如果找到了正在查找的键的索引元组, 使用获取的 TID 值读取所需的堆元组 。
每个 Tuple 包含两部分 的内容 ,一部分为 HeapTupleHeader,用来保存 Tuple 的元信息,如图 5-4 所示,包含该 Tuple 的 OID 、 xmin 、 cmin 等 ;另 一部分为 HeapTuple ,用来保存 Tuple 的数据 。
PG第五章-体系结构_第4张图片
PG第五章-体系结构_第5张图片

5.2. 进程结构

一用户一进程的客户端/服务器的应用程序 。 数据库启动时会启动若干
个进程,其中有 postmaster (守护进程)、 postgres (服务进程)、 syslogger 、 checkpointer 、bgwriter 、 walwriter 等辅助进程 。

5.2.1.守护进程与服务进程

postmaster 进程的主要职责有:

  • 数据库的启停 。
  • 监昕客户端连接 。
  • 为每个客户端连接 fork 单独的 postgres 服务进程 。
  • 当服务进程出错时进行修复 。
  • 管理数据文件 。
  • 管理与数据库运行相关的辅助进程 。
    当客户端调用接口库向数据库发起连接请求,守护进程 postmaster 会 fork 单独的服务进程 postgres 为客户端提供服务,此后将由 postgres 进程为客户端执行各种命令,客户端也不再需要 postmaster 中转,直接与服务进程 postgres 通信,直至客户端断开连接,如图 5 -6 所示 。
    PG第五章-体系结构_第6张图片
    PostgreSQL 使用基于消息的协议用于前端和后端(服务器和客户端)之间通信 。 通信都是通过一个消息流进行,消息的第一个字节标识消息类型,后面跟着的四个字节声明消息
    剩下部分的长度,该协议在 TCP/IP 和 Unix 域套接字上实现。 服务器作业之间通过信号和
    共享内存通信,以保证并发访问时的数据完整性。

5.2.2.辅助进程

除了守护进程 postmaster 和服务进程 postgres 外, PostgreSQL 在运行期间还需要一些
辅助进程才能工作,这些进程包括:

  • background writer :也可以称为 bgwriter 进程, bgwriter 进程很多时候都是在休眠状
    态,每次唤醒后它会搜索共享缓冲池找到被修改的页,并将它们从共享缓冲池刷出 。
  • autovacuum launcher : 自动清理回收垃圾进程 。
  • WALwriter :定期将 WAL 缓冲区上的 WAL 数据写入磁盘 。
  • statistics collector :统计信息收集进程 。
  • logging collector :日志进程,将消息或错误信息写入日志 。
  • archiver : WAL 归档进程 。
  • checkpointer :检查点进程 。
    图 5-8 显示了服务器端进程与辅助进程和 postmaster 守护进程的关系。
    PG第五章-体系结构_第7张图片

5.3. 内存结构

内存分为两大类:本地内存和共享内存,另外还有一些为辅助进程分配
的内存等

5.3.1.本地内存

本地内存由每个后端服务进程分配以供自己使用,当后端服务进程被 fork 时,每个后端进程为查询分配一个本地内存区域 。 本地内存由 三部分组成: work_mem 、 maintenance_work_mem 和 temp_buffers 。

  • work mem :当使用 ORDER BY 或 DISTINCT 操作对元组进行排序时会使用这部分内存。
  • maintenance work mem : 维护操作,例如 VACUUM 、 REINDEX 、 CREATE INDEX等操作使用这部分内存 。
  • temp_buffers :临时表相关操作使用这部分内存 。

5.3.2.共享内存

共享内存在 PostgreSQL 服务器启动时分配,由所有后端进程共同使用 。 共享内存主要由三部分组成 :

  • shared buffer pool : PostgreSQL 将表和索引中的页面从持久存储装载到这里 , 并直接操作它们 。
  • WAL buffer: WAL 文件持久化之前的缓冲区 。
  • CommitLog buffer : PostgreSQL 在 Commit Log 中保存事务的状态,并将这些状态保留在共享内存缓冲 区 中,在整个事务处理过程中使用 。
    PG第五章-体系结构_第8张图片

5.4.本章小结

PostgreSQL 数据库的文件存储 ,介绍了构成数据库的参数文件、数据文件布局,以及表空间、数据库、数据库对象的逻辑存储结构,简单介绍了
PostgreSQL 的守护进程 、服务进程和辅助进程,介绍了客户端与数据库服务器连接交互方式,从数据库目录到表文件到最小的数据块,从大到小逐层分析了重要的数据文件 。

你可能感兴趣的:(Postgres,数据库,postgresql)