物理文件
由一系列位于操作系统上的物理文件组成,通常将这些文理文件称为数据库。
逻辑文件
在数据库的运行中通过整套高效严谨的逻辑管理这些物理文件。
将物理文件、管理这些物理文件的进程、进程管理内存称为这个数据库的实例。
数据库集簇
数据库集簇由单个PostgreSQL服务器实例管理的数据库集合,组成数据库集簇的这些数据库使用相同的全局配置文件和监听端口、共用进程和内存结构。
创建一个Database时会为这个Database创建一个名为public的默认Schema,每个Database可以有多个Schema,在这个数据库中创建其他数据库对象时如果没有指定Schema,都会在public这个Schema中。Schema可以理解为一个数据库中的命名空间,在数据库中创建的所有对象都在Schema中创建,一个用户可以从同一个客户端连接中访问不同的Schema。不同的Schema中可以有多个相同名称的Table、Index、View、Sequence、Function等数据库对象。
数据库的文件默认保存在initdb时创建的数据目录中。在数据目录中有很多类型、功能不同的目录和文件,除了数据文件之外,还有参数文件、控制文件、数据库运行日志及预写日志等。
数据目录用来存放PostgreSQL持久化的数据,通常可以将数据目录路径配置为PGDATA环境变量,查看数据目录有哪些子目录和文件的命令如下所示:
目录文件:
[root@node03 ~]# tree -L 1 -d /var/lib/pgsql/12/data/
/var/lib/pgsql/12/data/
├── base
├── global
├── log
├── pg_commit_ts
├── pg_dynshmem
├── pg_logical
├── pg_multixact
├── pg_notify
├── pg_replslot
├── pg_serial
├── pg_snapshots
├── pg_stat
├── pg_stat_tmp
├── pg_subtrans
├── pg_tblspc
├── pg_twophase
├── pg_wal
└── pg_xact
18 directories
配置文件:
[root@node03 ~]# tree -L 1 -f /var/lib/pgsql/12/data/
/var/lib/pgsql/12/data
├── /var/lib/pgsql/12/data/base
├── /var/lib/pgsql/12/data/current_logfiles
├── /var/lib/pgsql/12/data/global
├── /var/lib/pgsql/12/data/log
├── /var/lib/pgsql/12/data/pg_commit_ts
├── /var/lib/pgsql/12/data/pg_dynshmem
├── /var/lib/pgsql/12/data/pg_hba.conf
├── /var/lib/pgsql/12/data/pg_ident.conf
├── /var/lib/pgsql/12/data/pg_logical
├── /var/lib/pgsql/12/data/pg_multixact
├── /var/lib/pgsql/12/data/pg_notify
├── /var/lib/pgsql/12/data/pg_replslot
├── /var/lib/pgsql/12/data/pg_serial
├── /var/lib/pgsql/12/data/pg_snapshots
├── /var/lib/pgsql/12/data/pg_stat
├── /var/lib/pgsql/12/data/pg_stat_tmp
├── /var/lib/pgsql/12/data/pg_subtrans
├── /var/lib/pgsql/12/data/pg_tblspc
├── /var/lib/pgsql/12/data/pg_twophase
├── /var/lib/pgsql/12/data/PG_VERSION
├── /var/lib/pgsql/12/data/pg_wal
├── /var/lib/pgsql/12/data/pg_xact
├── /var/lib/pgsql/12/data/postgresql.auto.conf
├── /var/lib/pgsql/12/data/postgresql.conf
├── /var/lib/pgsql/12/data/postmaster.opts
└── /var/lib/pgsql/12/data/postmaster.pid
18 directories, 8 files
数据目录中的base子目录是我们的数据文件默认保存的位置,是数据库初始化后的默认表空间。
OID:PostgreSQL中的所有数据库对象都由各自的对象标识符。OID 进行内部管理,它们是无符号的4字节整数。数据库对象和各个OID之间的关系存储在适当的系统目录中,具体取决于对象的类型。数据库的OID存储在pg_database系统表中。
mydb=# select oid, datname from pg_database where datname = 'mydb';
oid | datname
-------+---------
16384 | mydb
(1 row)
mydb=# select oid, relname, relkind from pg_class where relname ~ 'user';
oid | relname | relkind
-------+-----------------------------------+---------
174 | pg_user_mapping_oid_index | i
175 | pg_user_mapping_user_server_index | i
16432 | users_id_seq | S
16434 | users | r
16441 | users_pkey | i
1418 | pg_user_mapping | r
在PostgreSQL中最大的逻辑存储单位是表空间,数据库中创建的对象都保存在表空间中,例如表、索引和整个数据库都可以被分配到特定的农空间。#霭费猴距对象的文件的以指定数据库对象的表空间,如果不指定则使用默认表空间,也就是数据库对象的文件的位置。
初始化数据库目录时会自动创建pg_default和 pg_global两个表空间。如下所示:
mydb=# \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。
[root@node03 ~]# mkdir -p /pgdata/10/mytblspc
[root@node03 ~]# su - postgres
-bash-4.2$ /usr/pgsql-12/bin/psql mydb
mydb=# create tablespace myspc location '/pgdata/10/mytblspc';
CREATE TABLESPACE
mydb=# \db
List of tablespaces
Name | Owner | Location
------------+----------+---------------------
myspc | postgres | /pgdata/10/mytblspc
pg_default | postgres |
pg_global | postgres |
(3 rows)
mydb=# create table t1(id serial primary key, ival int) tablespace myspc;
CREATE TABLE
mydb=# \d t1
Table "public.t1"
Column | Type | Collation | Nullable | Default
--------+---------+-----------+----------+--------------------------------
id | integer | | not null | nextval('t1_id_seq'::regclass)
ival | integer | | |
Indexes:
"t1_pkey" PRIMARY KEY, btree (id)
Tablespace: "myspc"
由于表空间定义了存储的位置,在创建数据库对象时,会在当前的表空间目录创建一个以数据库OID命名的目录,该数据库的所有对象将保存在这个目录中,除非单独指定表空间。例如我们一直使用的数据库mydb,从pg_database系统表查询它的OID,如下所示:
mydb=# select oid,datname from pg_database where datname = 'mydb';
oid | datname
-------+---------
16384 | mydb
(1 row)
通过以上查询可知mydb 的OID为16384,我们就可以知道mydb的表、索引都会保存在$PGDATA/base/16384这个目录中,如下所示:
-bash-4.2$ ll /var/lib/pgsql/12/data/base/16384/ -d
drwx------ 2 postgres postgres 8192 Jul 8 16:38 /var/lib/pgsql/12/data/base/16384/
表看users表 OID
mydb=# select oid, relfilenode from pg_class where relname = 'users';
oid | relfilenode
-------+-------------
16434 | 16434
(1 row)
mydb-# \! ls -l /var/lib/pgsql/12/data/base/16384/16434
-rw------- 1 postgres postgres 8192 Jul 2 15:26 /var/lib/pgsql/12/data/base/16384/16434
mydb=# truncate users;
mydb=# checkpoint;
mydb=# \! ls -l /var/lib/pgsql/12/data/base/16384/16434
ls: cannot access /var/lib/pgsql/12/data/base/16384/16434: No such file or directory
mydb=# select oid, relfilenode from pg_class where relname = 'users';
oid | relfilenode
-------+-------------
16434 | 16479
(1 row)
mydb=# \! ls -l /var/lib/pgsql/12/data/base/16384/16479
-rw------- 1 postgres postgres 0 Jul 8 17:08 /var/lib/pgsql/12/data/base/16384/16479
如前文所述,数据文件的命名规则为,<顺序号>,tbl表的大小超过1GB,tbl表的relfilenode为24591,超出1GB之外的数据会按每GB切割,在文件系统中查看时就是名称为24591.1的数据文件。在上述输出结果中,后缀为_fsm和_vm的这两个表文件的附属文件是空闲空间映射表文件和可见性映射表文件。
空闲空间映射用来映射表文件中可用的空间;
可见性映射表文件跟踪哪些页面只包含已知对所有活动事务可见的元组,它也跟踪哪些页面只包含未被冻结的元组。
PostgreSQL数据目录、表空间以及文件的结构概貌:
在PostgreSQL中,将保存在磁盘中的块称为Page,而将内存中的块称为Buffer,表和索引称为Relation,行称为Tuple,如图5-3所示。数据的读写是以 Page为最小单位,每个Page默认大小为8kB,在编译PostgreSQL 时指定的BLCKSZ大小决定Page 的大小。每个表文件由多个BLCKSZ字节大小的Page组成,每个Page包含若干Tuple。对于I/O性能较好的硬件,并且以分析为主的数据库,适当增加BLCKSZ大小可以小幅提升数据库性能。
page 内部结构
PostgreSQL是一用户一进程的客户端/服务器的应用程序。数据库启动时会启动若干个进程,其中有postmaster(守护进程)、postgres(服务进程)、syslogger、checkpointer,bgwriter、walwriter等辅助进程。
postmaster 职责:
当客户端调用接口库向数据库发起连接请求,守护进程postmaster 会 fork单独的服务进程postgres为客户端提供服务,此后将由postgres进程为客户端执行各种命令,客户端也不再需要postmaster 中转,直接与服务进程postgres通信,直至客户端断开连接。
PostgreSQL使用基于消息的协议用于前端和后端(服务器和客户端)之间通信。通信都是通过一个消息流进行,消息的第一个字节标识消息类型,后面跟着的四个字节声明消息剩下部分的长度,该协议在TCP/IP和Unix域套接字上实现。服务器作业之间通过信号和共享内存通信,以保证并发访问时的数据完整性。
除了守护进程postmaster和服务进程postgres外,PostgreSQL在运行期间还需要一些辅助进程才能工作,这些进程包括:
服务器端进程与辅助进程和 postmaster守护进程的关系:
PostgreSQL的内存分为两大类:本地内存和共享内存,另外还有一些为辅助进程分配的内存等。
本地内存由每个后端服务进程分配以供自己使用,当后端服务进程被fork时,每个后端进程为查询分配一个本地内存区域。本地内存由三部分组成: work_mem、maintwork_mem和 temp_buffers。
共享内存在PostgreSQL服务器启动时分配,由所有后端进程共同使用。共享内存主要由三部分组成:
内存结构: