Linux,  PostgreSQL

PostgreSQL数据库体系架构

总体来讲,PostgreSQL数据库的体系架构比较复杂,但是相较于更复杂的Oracle数据库的体系架构而言,还是相对比较容易上手,但是,也需要花一些时间和精力。我们可以从进程结构、内存结构、存储结构3个方面来学习和研究。

一 PostgreSQL进程结构

1 进程结构图谱和分类
PostgreSQL process architecture

PostgreSQL数据库的进程可以分为三类:后台进程、后端进程或叫服务器进程、客户端进程或用户进程。

a 后台进程:
[postgres@iZbp1anc2b2vggfj0i0oovZ ~]$ id
uid=1000(postgres) gid=1000(postgres) groups=1000(postgres)
[postgres@iZbp1anc2b2vggfj0i0oovZ ~]$ ps -ef|grep postgres
postgres 21928     1  0 Mar29 ?        00:00:01 /data/postgres/13.2/bin/postgres
postgres 21930 21928  0 Mar29 ?        00:00:00 postgres: checkpointer
postgres 21931 21928  0 Mar29 ?        00:00:01 postgres: background writer
postgres 21932 21928  0 Mar29 ?        00:00:01 postgres: walwriter
postgres 21933 21928  0 Mar29 ?        00:00:01 postgres: autovacuum launcher
postgres 21934 21928  0 Mar29 ?        00:00:02 postgres: stats collector
postgres 21935 21928  0 Mar29 ?        00:00:00 postgres: logical replication launcher
root     31003 31002  0 10:14 pts/2    00:00:00 su - postgres
postgres 31004 31003  0 10:14 pts/2    00:00:00 -bash
postgres 31032 31004  0 10:14 pts/2    00:00:00 ps -ef
postgres 31033 31004  0 10:14 pts/2    00:00:00 grep --color=auto postgres
[postgres@iZbp1anc2b2vggfj0i0oovZ ~]$ pstree -p 21928
postgres(21928)─┬─postgres(21930)
              ├─postgres(21931)
              ├─postgres(21932)
              ├─postgres(21933)
              ├─postgres(21934)
              └─postgres(21935)
[postgres@iZbp1anc2b2vggfj0i0oovZ ~]$

其中,/data/postgres/13.2/bin/postgres是数据库服务器的master进程,其它诸如checkpoint,background writer,walwrite,autovacuum launcher,stats collector,logical replication launcher都是由它fork的子进程。当然,数据库运行模式不同,配置不同,也可能有其它后台进程,如归档进程等。

postgres:PostgreSQL数据库的核心进程,也是整个cluster的父进程,该进程出现问题,整个cluster就over了。该进程由操作系统的守护进程1号进程派生。Linux下的1号进程是整个服务器的守护进程,类比于Java程序中的Object类,一切类的父类。

checkpointer:检查点进程,等价于Oracle的CKPT进程,负责完成数据库的检查点,通知数据库的写进程DBWR将内存中的脏数据写出到磁盘。

background writer :等价于Oracle的DBWR进程,负责将内存中的脏数据写出到磁盘。

walwriter :等价于Oracle的LGWR进程,负责将日志缓冲区中的记录关于数据库的修改的日志写出到日志文件中去,确保数据的修改不会丢失,用于恢复使用。

autovacuum launcher:自动清理工作进程。由于PostgreSQL不像Oracle那样有undo的机制,将数据被修改前的信息写入到undo,然后修改数据。PostgreSQL采取的是在原数据块上进行保留旧的数据,并作标记,等到将来修改提交生效之后,旧的数据(dead tuple翻译为死元组)不需要的话,就得清理,由该进程来完成。

stats collector:统计信息收集进程。用于及时的更新数据库中的统计信息,如表、index有多少条记录,数据分布等,给优化器提供最新的信息,便于优化器选择最优的执行计划。避免统计信息不准确,导致优化器选择错误的执行计划,导致SQL性能下降或偏差。

logical replication launcher:逻辑复制进程。用于完成逻辑复制的工作。

archiver:归档进程,等价于Oracle的ARCH进程,用于完成数据库日志文件的归档。当数据库配置了归档模式之后,可以看到该进程。

b 后端进程(backend)或服务器进程:

当我们的应用程序和图形界面的客户端工具,连接到PostgreSQL数据库服务器时。master进程会为该应用程序创建1个服务器进程,用于处理和响应该客户端应用程序的请求。

[postgres@iZbp1anc2b2vggfj0i0oovZ ~]$ psql
psql (13.2)
Type "help" for help.

postgres=# select pg_backend_pid();
pg_backend_pid
----------------
         31072
(1 row)

postgres=# \! ps -ef|grep postgres
postgres 21928     1  0 Mar29 ?        00:00:01 /data/postgres/13.2/bin/postgres
postgres 21930 21928  0 Mar29 ?        00:00:00 postgres: checkpointer
postgres 21931 21928  0 Mar29 ?        00:00:01 postgres: background writer
postgres 21932 21928  0 Mar29 ?        00:00:01 postgres: walwriter
postgres 21933 21928  0 Mar29 ?        00:00:01 postgres: autovacuum launcher
postgres 21934 21928  0 Mar29 ?        00:00:02 postgres: stats collector
postgres 21935 21928  0 Mar29 ?        00:00:00 postgres: logical replication launcher
root     31003 31002  0 10:14 pts/2    00:00:00 su - postgres
postgres 31004 31003  0 10:14 pts/2    00:00:00 -bash
postgres 31071 31004  0 10:21 pts/2    00:00:00 psql
postgres 31072 21928  0 10:21 ?        00:00:00 postgres: postgres postgres [local] idle
postgres 31075 31071  0 10:22 pts/2    00:00:00 sh -c ps -ef|grep postgres
postgres 31076 31075  0 10:22 pts/2    00:00:00 ps -ef
postgres 31077 31075  0 10:22 pts/2    00:00:00 grep postgres
postgres=#

如上,可以看到服务器进程号是31072,通过select pg_backend_pid()查询。同时,看到服务器上该进程的父进程是21928,由/data/postgres/13.2/bin/postgres这个主进程派生。

后端进程或服务器进程的数量由max_connections参数决定。

每一个后端进程一次只能访问一个数据库。它和客户端进程进行TCP通信,开户端断开之后,该进程自动回收消失。客户端重新连接或发起新连接时重新创建新的后端进程。

由于进程的创建或回收,比较消耗操作系统的资源,因此,多数情况下,应用系统都会通过连接池的方式和数据库建立连接。

从PostgreSQL官方给后端进程的命名可以看到还是一脉相承的,比如,我们查看当前会话所在的后端进程号或者叫服务器进程的时候,我们调用的是pg_backend_pid()函数,杀会话所在进程时,调用的是pg_terminate_backend(),或者pg_cancel_backend()。

[postgres@iZbp1anc2b2vggfj0i0oovZ ~]$ psql
psql (13.2)
Type "help" for help.
​
postgres=# select pg_backend_pid();
 pg_backend_pid 
----------------
          26933
(1 row)
​
postgres=# select pg_terminate_backend(26933);
FATAL:  terminating connection due to administrator command
server closed the connection unexpectedly
        This probably means the server terminated abnormally
        before or while processing the request.
The connection to the server was lost. Attempting reset: Succeeded.
postgres=# \c
You are now connected to database "postgres" as user "postgres".
postgres=#
c 用户进程或客户端进程:

指的是连接数据库服务器的应用程序或者客户端工具等。

c 用户进程或客户端进程:

指的是连接数据库服务器的应用程序或者客户端工具等。

d 用户进程连接示意图
用户进程连接示意图

每个用户进程或者客户端进程对应一个服务端进程。

用户进程–服务端进程:1对1的关系

这2张图引自《PostgreSQL for DBA Architects》p21-22。

2 数据库服务器启动流程

当我们通过pg_ctl工具来启动PostgreSQL数据库时,先在操作系统上创建1个master进程,然后该进程派生出一系列的后台进程,同时该进程监听$PGDATA/postgresql.conf配置文件中指定的端口。并且,向操作系统申请内存,用于数据库的正常运行操作,处理客户端的连接请求操作处理。最后,数据库可以正常对外提供服务。

[postgres@iZbp1anc2b2vggfj0i0oovZ ~]$ netstat -anp|grep 5432
(Not all processes could be identified, non-owned process info
will not be shown, you would have to be root to see it all.)
tcp        0      0 127.0.0.1:5432          0.0.0.0:*               LISTEN      21928/postgres      
tcp6       0      0 ::1:5432               :::*                   LISTEN      21928/postgres      
unix  2     [ ACC ]     STREAM     LISTENING     7523185  21928/postgres       /tmp/.s.PGSQL.5432
[postgres@iZbp1anc2b2vggfj0i0oovZ ~]$ psql
psql (13.2)
Type "help" for help.

postgres=# show listen_addresses ;
listen_addresses
------------------
localhost
(1 row)

postgres=#

由于我这边的$PGDATA/postgresql.conf配置文件中指定的端口是5432,且listen_address=localhost,只对本机环路地址127.0.0.1监听,所以,看到master主进程21928监听在127.0.0.1:5432。如果listen_address=*,监听本机所有地址的话,则,看到类似下述结果,源于另外一套数据库环境:

postgres=# show listen_addresses ;
listen_addresses
------------------
*
(1 row)

postgres=# \q
[pg13@centos-master ~]$ netstat -anp|grep 5433
(Not all processes could be identified, non-owned process info
will not be shown, you would have to be root to see it all.)
tcp        0      0 0.0.0.0:5433            0.0.0.0:*               LISTEN      -                  
tcp6       0      0 :::5433                 :::*                   LISTEN      -                  
unix  2     [ ACC ]     STREAM     LISTENING     984821916 -                   /tmp/.s.PGSQL.5433
[pg13@centos-master ~]$
3 进程结构小结

PostgreSQL数据库服务器的进程结构,类似于Oracle数据库的进程结构:守护进程,后台进程,服务器进程,用户进程等。

二 PostgreSQL内存结构

1内存结构图谱
PostgreSQL memory architecture
2内存结构分类

内存结构分为共享内存、本地内存。类似于Oracle的SGA和PGA。

a共享内存

是指数据库服务器向操作系统申请的共享内存段,如数据共享缓冲区、日志缓冲区、事务提交日志内存区等,提供给PostgreSQL服务器的所有进程使用。

数据共享缓冲区:PostgreSQL把要操作和处理的表、index,读入到内存中,放到该区域缓存。类似于Oracle的database buffer cache。其大小由shared_buffers参数决定。

日志缓冲区:用于缓存数据库中对数据修改的日志记录,如:update table test set id=1这条SQL语句,数据库会把这个操作的信息记录在该内存区,将来写出到日志文件中,如果配置为归档模式,则最终写出到归档日志文件中去,用于恢复使用。其大小由wal_buffers参数决定。类似于Oracle的log buffer。

提交日志缓冲区:该内存区域有别于wal buffer日志缓冲区。它用于记录数据库中所有事务的提交状态,事务是否已经提交,是否已经终止,是否进行中,子事务等状态信息。用于MVCC。

There is no specific parameter to control this area of memory. This is automatically managed by the database engine in tiny amounts. This is a shared memory component, which is accessible to all the background server and user processes of a PostgreSQL database.

b本地内存

当我们和数据库建立一个连接请求时,数据库帮我们创建1个后端进程。并给该后端进程分配的内存区域,该内存区域只属于这一个后端进程使用,可以认为是私有的。用于处理和响应我们向数据库发起的请求操作。通常包含:工作区work mem、维护工作区、临时缓冲区。

工作区:该内存区用于处理客户端SQL语句请求的order by排序、distinct过滤、表合并连接merge-join、哈希连接hash-join操作等。由work_mem参数决定大小。

维护工作区:该内存区域用于处理重建索引reindex、vacuum空间回收操作、给表添加外键约束等。由maintenance_work_mem参数决定大小。

临时缓冲区:该内存区用于创建和访问临时表时,存放临时表的数据。该内存区和因为SQL中因为大表排序或hash table而在服务器上建立的临时文件(位于pgsql_tmp路径下)没有直接关系。由temp_buffers参数决定大小。

三 PostgreSQL数据库的存储结构

1 PostgreSQL逻辑存储结构
a 什么是PostgreSQL cluster

当我们在一台服务器上安装部署并且初始化一个PostgreSQL数据库之后,严格的讲,其实是我们安装部署了一套PostgreSQL数据库软件,然后初始化了一个PostgreSQL的database cluster。这里的cluster是什么概念呢?

首先,这里的cluster完全是个逻辑上的概念,它是指一系列的数据库的集合。它所包含的数据库就是指,当我们以pg_ctl -D /data/postgres/13.2/data start来启动数据库cluster时,由这个-D参数指定,或者是PGDATA环境变量指定的路径下的所有的数据库的集合。当然,这么说太抽象了,其实,从物理上,我们可以这么说明和解释:

[postgres@iZbp1anc2b2vggfj0i0oovZ ~]$ env|grep PGDATA
PGDATA=/data/postgres/13.2/data
[postgres@iZbp1anc2b2vggfj0i0oovZ ~]$ ll /data/postgres/13.2/data/base/
total 48
drwx------ 2 postgres postgres 8192 Mar 26 11:26 1
drwx------ 2 postgres postgres 8192 Mar 26 11:26 12722
drwx------ 2 postgres postgres 8192 Mar 29 16:36 12723
drwx------ 2 postgres postgres 8192 Mar 29 16:37 16393
[postgres@iZbp1anc2b2vggfj0i0oovZ ~]$ oid2name
All databases:
  Oid Database Name Tablespace
----------------------------------
 12723       postgres pg_default
 12722     template0 pg_default
     1     template1 pg_default
 16393         testdb pg_default
[postgres@iZbp1anc2b2vggfj0i0oovZ ~]$ psql
psql (13.2)
Type "help" for help.

postgres=# \l
                                List of databases
  Name   | Owner   | Encoding |   Collate   |   Ctype   |   Access privileges  
-----------+----------+----------+-------------+-------------+-----------------------
postgres | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 |
template0 | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 | =c/postgres         +
          |         |         |             |             | postgres=CTc/postgres
template1 | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 | =c/postgres         +
          |         |         |             |             | postgres=CTc/postgres
testdb   | t_user   | UTF8     | en_US.UTF-8 | en_US.UTF-8 |
(4 rows)

postgres=# select oid,datname from pg_database ;
oid | datname  
-------+-----------
12723 | postgres
    1 | template1
12722 | template0
16393 | testdb
(4 rows)

postgres=#
  • 我们的PGDATA=/data/postgres/13.2/data,就是说,我们这个PostgreSQL cluster下的所有数据库都在这个路径下;
  • 查看/data/postgres/13.2/data/base/,可以看到一系列的数字命名的文件,其实它们就是这个PostgreSQL cluster所包含的所有的数据库;这些数字化的文件名,其实就是数据库的oid(object identifier),相当于数据库的编号,这个是PostgreSQL数据库本身自有的特性;
  • 执行oid2name,可以看到数据库名和oid之间的对应关系;
  • 进入psql,也可以通过\l命令列出当前PostgreSQL cluster下的所有的数据库集合;
  • 从pg_database字典表中,也可以查询到当前PostgreSQL cluster下的所有的数据库集合的信息;

因此,我们说PostgreSQL cluster它是一个逻辑概念,它包含了一个或多个PostgreSQL数据库。一个数据库只能属于一个cluster。补充,这里的cluster跟Oracle Real Application Cluster里的cluster完全不挨着,是两个完全不同的概念。

b database和cluster的关系

数据库是指一些列数据库对象的集合,比如表,index,view,function等这些数据库对象隶属于一个特定的数据库。

cluster指的是一些列数据库的集合。比如:一个cluster初始化之后,包含3个默认数据库:postgres,默认的管理数据库;template0,默认的不可修改的空数据库;template1,默认的模板数据库,当我们创建数据库时,会参照该数据库来创建。

当我们在template1模板数据库中创建和安装1个数据库插件extension,uuid之后,再去创建新的数据库,那么新数据库中就会自动包含该uuid这个extension。

1个cluster可以包含多个数据库,反过来1个database只能隶属于1个cluster。

PostgreSQL cluster vs databases
c 数据库实例和cluster的关系

我们定义数据库实例是指一堆PostgreSQL的后台进程和内存结构,cluster指的是我们在初始化数据库时,指定的PGDATA环境变量指向的操作系统上的那个路径下的一堆的文件。

一个数据库实例在其一个生命周期内(从启动到关闭)只能“挂载”一个数据库cluster,反之,一个cluster也只能被一个实例挂载访问。二者之间是严格的一对一关系。

但是,在一台服务器上,我们可以安装一套PostgreSQL数据库软件,用这个数据库软件可以创建多个实例和多个cluster。每个实例对应于一个cluster。只要每个cluster所指定的监听端口不同,我们就可以同时运行多个实例和cluster。

如:我们可以在这套环境上再初始化一个实例和cluster,将其指向另外一个监听端口即可。

[postgres@iZbp1anc2b2vggfj0i0oovZ ~]$ echo $PGDATA
/data/postgres/13.2/data
[postgres@iZbp1anc2b2vggfj0i0oovZ ~]$ ll /data/postgres/13.2/
total 20
drwxrwxr-x  2 postgres postgres 4096 Mar 26 11:24 bin
drwx------ 19 postgres postgres 4096 Mar 29 14:49 data
drwxrwxr-x  6 postgres postgres 4096 Mar 26 11:24 include
drwxrwxr-x  4 postgres postgres 4096 Mar 26 11:24 lib
drwxrwxr-x  8 postgres postgres 4096 Mar 26 11:24 share
[postgres@iZbp1anc2b2vggfj0i0oovZ ~]$

创建和初始化一个新的cluster,让其指向/data/postgres/13.2/newdata

[postgres@iZbp1anc2b2vggfj0i0oovZ ~]$ /data/postgres/13.2/bin/initdb -D /data/postgres/13.2/newdata
The files belonging to this database system will be owned by user "postgres".
This user must also own the server process.

The database cluster will be initialized with locale "en_US.UTF-8".
The default database encoding has accordingly been set to "UTF8".
The default text search configuration will be set to "english".

Data page checksums are disabled.

creating directory /data/postgres/13.2/newdata ... ok
creating subdirectories ... ok
selecting dynamic shared memory implementation ... posix
selecting default max_connections ... 100
selecting default shared_buffers ... 128MB
selecting default time zone ... Asia/Shanghai
creating configuration files ... ok
running bootstrap script ... ok
performing post-bootstrap initialization ... ok
syncing data to disk ... ok

initdb: warning: enabling "trust" authentication for local connections
You can change this by editing pg_hba.conf or using the option -A, or
--auth-local and --auth-host, the next time you run initdb.

Success. You can now start the database server using:

  /data/postgres/13.2/bin/pg_ctl -D /data/postgres/13.2/newdata -l logfile start

[postgres@iZbp1anc2b2vggfj0i0oovZ ~]$ ll /data/postgres/13.2/
total 24
drwxrwxr-x  2 postgres postgres 4096 Mar 26 11:24 bin
drwx------ 19 postgres postgres 4096 Mar 29 14:49 data
drwxrwxr-x  6 postgres postgres 4096 Mar 26 11:24 include
drwxrwxr-x  4 postgres postgres 4096 Mar 26 11:24 lib
drwx------ 19 postgres postgres 4096 Mar 31 16:22 newdata
drwxrwxr-x  8 postgres postgres 4096 Mar 26 11:24 share
[postgres@iZbp1anc2b2vggfj0i0oovZ ~]$

修改/data/postgres/13.2/newdata/postgresql.conf 中的port = 5433 ,然后就可以启动一个新的实例和cluster。启动时,显示指定新的PGDATA=/data/postgres/13.2/newdata/即可。

[postgres@iZbp1anc2b2vggfj0i0oovZ ~]$ pg_ctl start -D /data/postgres/13.2/newdata/
waiting for server to start....2021-03-31 16:26:01.970 CST [32686] LOG: starting PostgreSQL 13.2 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 8.3.1 20190507 (Red Hat 8.3.1-4), 64-bit
2021-03-31 16:26:01.970 CST [32686] LOG: listening on IPv6 address "::1", port 5433
2021-03-31 16:26:01.970 CST [32686] LOG: listening on IPv4 address "127.0.0.1", port 5433
2021-03-31 16:26:01.973 CST [32686] LOG: listening on Unix socket "/tmp/.s.PGSQL.5433"
2021-03-31 16:26:01.976 CST [32687] LOG: database system was shut down at 2021-03-31 16:22:25 CST
2021-03-31 16:26:01.978 CST [32686] LOG: database system is ready to accept connections
done
server started
[postgres@iZbp1anc2b2vggfj0i0oovZ ~]$ ps -ef|grep postgres
postgres 21928     1  0 Mar29 ?        00:00:01 /data/postgres/13.2/bin/postgres
postgres 21930 21928  0 Mar29 ?        00:00:00 postgres: checkpointer
postgres 21931 21928  0 Mar29 ?        00:00:01 postgres: background writer
postgres 21932 21928  0 Mar29 ?        00:00:01 postgres: walwriter
postgres 21933 21928  0 Mar29 ?        00:00:01 postgres: autovacuum launcher
postgres 21934 21928  0 Mar29 ?        00:00:02 postgres: stats collector
postgres 21935 21928  0 Mar29 ?        00:00:00 postgres: logical replication launcher
root     31003 31002  0 10:14 pts/2    00:00:00 su - postgres
postgres 31004 31003  0 10:14 pts/2    00:00:00 -bash
root     31195 31194  0 10:43 pts/1    00:00:00 su - postgres
postgres 31196 31195  0 10:43 pts/1    00:00:00 -bash
postgres 32686     1  0 16:26 ?        00:00:00 /data/postgres/13.2/bin/postgres -D /data/postgres/13.2/newdata
postgres 32688 32686  0 16:26 ?        00:00:00 postgres: checkpointer
postgres 32689 32686  0 16:26 ?        00:00:00 postgres: background writer
postgres 32690 32686  0 16:26 ?        00:00:00 postgres: walwriter
postgres 32691 32686  0 16:26 ?        00:00:00 postgres: autovacuum launcher
postgres 32692 32686  0 16:26 ?        00:00:00 postgres: stats collector
postgres 32693 32686  0 16:26 ?        00:00:00 postgres: logical replication launcher
postgres 32695 31196  0 16:26 pts/1    00:00:00 ps -ef
postgres 32696 31196  0 16:26 pts/1    00:00:00 grep --color=auto postgres
[postgres@iZbp1anc2b2vggfj0i0oovZ ~]$ netstat -anp|grep postgres
(Not all processes could be identified, non-owned process info
will not be shown, you would have to be root to see it all.)
tcp        0      0 127.0.0.1:5432          0.0.0.0:*               LISTEN      21928/postgres      
tcp        0      0 127.0.0.1:5433          0.0.0.0:*               LISTEN      32686/postgres      
tcp6       0      0 ::1:5432               :::*                   LISTEN      21928/postgres      
tcp6       0      0 ::1:5433               :::*                   LISTEN      32686/postgres      
udp6       0      0 ::1:37716               ::1:37716               ESTABLISHED 32686/postgres      
udp6       0      0 ::1:41028               ::1:41028               ESTABLISHED 21928/postgres      
unix  2     [ ACC ]     STREAM     LISTENING     7523185  21928/postgres       /tmp/.s.PGSQL.5432
unix  2     [ ACC ]     STREAM     LISTENING     7618463  32686/postgres       /tmp/.s.PGSQL.5433
[postgres@iZbp1anc2b2vggfj0i0oovZ ~]$

从上,我们看到在一台数据库服务器上,同时启动了2个数据库实例和cluster,只是监听在不同的端口罢了。同样,当我们想要关闭这个新的实例和cluster时,也需要显示指定其PGDATA。

[postgres@iZbp1anc2b2vggfj0i0oovZ ~]$ pg_ctl stop -m fast -D /data/postgres/13.2/newdata/
waiting for server to shut down....2021-03-31 16:30:29.546 CST [32686] LOG: received fast shutdown request
2021-03-31 16:30:29.547 CST [32686] LOG: aborting any active transactions
2021-03-31 16:30:29.548 CST [32686] LOG: background worker "logical replication launcher" (PID 32693) exited with exit code 1
2021-03-31 16:30:29.549 CST [32688] LOG: shutting down
2021-03-31 16:30:29.556 CST [32686] LOG: database system is shut down
done
server stopped
[postgres@iZbp1anc2b2vggfj0i0oovZ ~]$
d PostgreSQL的tablespace
  • 定义:tablespace依然是一个逻辑概念,它是隶属于cluster的。
  • 查看:通过pg_tablespace字典表来查看cluster下表空间的信息,或者是\db命令也可查看表空间的信息;[postgres@iZbp1anc2b2vggfj0i0oovZ ~]$ psql
    psql (13.2)
    Type “help” for help.

    postgres=# \db+
                                    List of tablespaces
      Name   | Owner   | Location | Access privileges | Options | Size | Description
    ————+———-+———-+——————-+———+——–+————-
    pg_default | postgres |         |                   |         | 29 MB |
    pg_global | postgres |         |                   |         | 559 kB |
    (2 rows)
    postgres=# select oid,spcname from pg_tablespace;
    oid | spcname  
    ——+————
    1663 | pg_default
    1664 | pg_global
    (2 rows)

    postgres=#
  • 默认表空间:每个cluster默认情况下有2个表空间,分别命名为pg_default用于存放各个数据库私有的数据库对象,pg_global用于存放cluster全局共享的数据库对象信息,例如:cluster中数据库本身的信息,表空间的信息,数据库订阅信息,数据库复制信息,数据库认证授权信息、控制文件等。
  • 专用表空间:pg_global表空间是专表空间专用的,只能存放全局共享的数据库对象,不能存放用户数据,否则报错:
postgres=# \db
      List of tablespaces
  Name   | Owner   | Location
------------+----------+----------
pg_default | postgres |
pg_global | postgres |
(2 rows)
postgres=# create table t1(id int) tablespace pg_global;
ERROR: only shared relations can be placed in pg_global tablespace
postgres=#

pg_default,表空间,默认存放cluster下所有数据库的所有数据库对象。这一点儿上,倒是有点儿类似于Oracle数据库的专表空间专用。

  • 表空间和数据库的关系:一个表空间可以给多个数据库使用,一个数据库里的不同数据库对象也可以存放在不同的表空间下。表空间和数据库的关系,不严格的讲,可以说是多对多的关系。不像Oracle数据库中,一个数据库可以包含多个表空间,且每个表空间只能属于一个数据库使用。
  • 如何创建表空间:OS上路径需提前创建,postgres用户得有读写操作系统文件系统权限,数据库管理员权限
[postgres@iZbp1anc2b2vggfj0i0oovZ ~]$ pwd
/home/postgres
[postgres@iZbp1anc2b2vggfj0i0oovZ ~]$ ll
total 26916
-rw-rw-r-- 1 postgres postgres       26 Mar 29 15:56 ext.sql
drwxrwxr-x 6 postgres postgres     4096 Mar 26 11:20 postgresql-13.2
-rw-rw-r-- 1 postgres postgres 27548921 Feb 9 06:07 postgresql-13.2.tar.gz
-rw------- 1 postgres postgres     2619 Mar 26 11:39 startup.log
[postgres@iZbp1anc2b2vggfj0i0oovZ ~]$ mkdir tbs_dir
[postgres@iZbp1anc2b2vggfj0i0oovZ ~]$ ll
total 26916
-rw-rw-r-- 1 postgres postgres       26 Mar 29 15:56 ext.sql
drwxrwxr-x 6 postgres postgres     4096 Mar 26 11:20 postgresql-13.2
-rw-rw-r-- 1 postgres postgres 27548921 Feb 9 06:07 postgresql-13.2.tar.gz
-rw------- 1 postgres postgres     2619 Mar 26 11:39 startup.log
drwxrwxr-x 2 postgres postgres        6 Mar 30 20:34 tbs_dir
[postgres@iZbp1anc2b2vggfj0i0oovZ ~]$ psql
psql (13.2)
Type "help" for help.

postgres=# create tablespace newtbs location '/home/postgres/tbs_dir';
CREATE TABLESPACE
postgres=# \db+
                                        List of tablespaces
  Name   | Owner   |       Location       | Access privileges | Options | Size   | Description
------------+----------+------------------------+-------------------+---------+---------+-------------
newtbs     | postgres | /home/postgres/tbs_dir |                   |         | 0 bytes |
pg_default | postgres |                       |                   |         | 29 MB   |
pg_global | postgres |                       |                   |         | 559 kB |
(3 rows)

postgres=# select oid,spcname from pg_tablespace;
oid | spcname  
-------+------------
 1663 | pg_default
 1664 | pg_global
16397 | newtbs
(3 rows)

postgres=#

postgres用户,在家目录下创建了一个tbs_dir,保证postgres用户对该路径的读写权限,以postgres用户访问postgres数据库(即以数据库管理员身份访问系统数据库,关于数据库的更多使用,后面再讲)。查看到,目前数据库内部自带两个默认表空间pg_default和pg_global。通过create tablespace newtbs location ‘/home/postgres/tbs_dir’;创建名为newtbs的表空间,存储在/home/postgres/tbs_dir路径下,见名知意,直观易懂。

  • 如何使用表空间:由于表空间和数据库是不严格的多对多的关系,所以,我们可以在创建数据库的时候,指定数据库的表空间存储信息,也可以在创建数据库对象时,指定其存储的所在表空间。

创建数据库对象时,显示指定表空间信息,如create table test_tbs(id int) tablespace newtbs ;

postgres=# \c
You are now connected to database "postgres" as user "postgres".
postgres=# create table test_tbs(id int) tablespace newtbs ;
CREATE TABLE
postgres=# \d test_tbs
             Table "public.test_tbs"
Column | Type   | Collation | Nullable | Default
--------+---------+-----------+----------+---------
id     | integer |           |         |
Tablespace: "newtbs"

postgres=#

创建数据库时,指定其存储所在的表空间信息:

postgres=# create database newdb tablespace newtbs;
CREATE DATABASE
postgres=#

当然,数据库创建之后,或者数据库对象(表、index等)创建之后,也可以分别通过alter database/table/index来修改表空间信息。

  • 作用:主要用于逻辑上隔离数据库对象,或者用于数据库存储空间规划或迁移存储。想要通过表空间的设置,进而对于数据库性能提升?作用不大,毕竟现在基本上都是直接上SSD(Solid State Drive )存储给数据库使用。
2 PostgreSQL物理存储结构
a cluster的物理结构图谱
PostgreSQL cluster map to filesystem
b cluster在文件系统上的结构

我们知道一个cluster实质上是操作系统上的一个路径,该路径下包含了若干个文件。

[postgres@iZbp1anc2b2vggfj0i0oovZ ~]$ echo $PGDATA
/data/postgres/13.2/data
[postgres@iZbp1anc2b2vggfj0i0oovZ ~]$ ll /data/postgres/13.2/data/
total 60
drwx------ 6 postgres postgres    54 Mar 29 16:37 base
drwx------ 2 postgres postgres  4096 Mar 30 20:50 global
drwx------ 2 postgres postgres     6 Mar 26 11:26 pg_commit_ts
drwx------ 2 postgres postgres     6 Mar 26 11:26 pg_dynshmem
-rw------- 1 postgres postgres  4760 Mar 26 11:26 pg_hba.conf
-rw------- 1 postgres postgres  1636 Mar 26 11:26 pg_ident.conf
drwx------ 4 postgres postgres    68 Mar 31 16:13 pg_logical
drwx------ 4 postgres postgres    36 Mar 26 11:26 pg_multixact
drwx------ 2 postgres postgres     6 Mar 26 11:26 pg_notify
drwx------ 2 postgres postgres     6 Mar 26 11:26 pg_replslot
drwx------ 2 postgres postgres     6 Mar 26 11:26 pg_serial
drwx------ 2 postgres postgres     6 Mar 26 11:26 pg_snapshots
drwx------ 2 postgres postgres     6 Mar 29 14:29 pg_stat
drwx------ 2 postgres postgres   105 Apr  1 09:17 pg_stat_tmp
drwx------ 2 postgres postgres    18 Mar 26 11:26 pg_subtrans
drwx------ 2 postgres postgres    19 Mar 30 20:41 pg_tblspc
drwx------ 2 postgres postgres     6 Mar 26 11:26 pg_twophase
-rw------- 1 postgres postgres     3 Mar 26 11:26 PG_VERSION
drwx------ 3 postgres postgres    60 Mar 26 11:26 pg_wal
drwx------ 2 postgres postgres    18 Mar 26 11:26 pg_xact
-rw------- 1 postgres postgres    88 Mar 26 11:26 postgresql.auto.conf
-rw------- 1 postgres postgres 28011 Mar 26 11:26 postgresql.conf
-rw------- 1 postgres postgres    33 Mar 29 14:29 postmaster.opts
-rw------- 1 postgres postgres    91 Mar 29 14:29 postmaster.pid
[postgres@iZbp1anc2b2vggfj0i0oovZ data]$ ll base/
total 48
drwx------ 2 postgres postgres 8192 Mar 26 11:26 1
drwx------ 2 postgres postgres 8192 Mar 26 11:26 12722
drwx------ 2 postgres postgres 8192 Mar 29 16:36 12723
drwx------ 2 postgres postgres 8192 Mar 29 16:37 16393
[postgres@iZbp1anc2b2vggfj0i0oovZ data]$ oid2name
All databases:
  Oid Database Name Tablespace
----------------------------------
 16401         newdb     newtbs
 12723       postgres pg_default
 12722     template0 pg_default
     1     template1 pg_default
 16393         testdb pg_default
[postgres@iZbp1anc2b2vggfj0i0oovZ data]$ ll /home/postgres/tbs_dir/PG_13_202007201/
total 12
drwx------ 2 postgres postgres   19 Mar 30 20:44 12723
drwx------ 2 postgres postgres 8192 Mar 31 10:00 16401
[postgres@iZbp1anc2b2vggfj0i0oovZ data]$

$PGDATA/base:用于存放当前cluster下所有的数据库,数字化命名的路径表示各个数据库,每个数字表示数据库的oid。最重要!oid2name列出的16401这个newdb数据库不在$PGDATA/base路径下,而在/home/postgres/tbs_dir/PG_13_202007201/的原因是,前面我们新建这个数据库的时候,将其存储指定为newtbs下。我们只需执行alter database newdb tablespace pg_default;就可以将其从newtbs表空间迁移至pg_default表空间下。进而,其物理上也将自动从/home/postgres/tbs_dir/PG_13_202007201/迁移至$PGDATA/base路径:

[postgres@iZbp1anc2b2vggfj0i0oovZ data]$ psql 
psql (13.2)
Type "help" for help.

postgres=# alter database newdb tablespace pg_default;
ALTER DATABASE
postgres=# \q
[postgres@iZbp1anc2b2vggfj0i0oovZ data]$ ll /home/postgres/tbs_dir/PG_13_202007201/
total 0
drwx------ 2 postgres postgres 19 Mar 30 20:44 12723
[postgres@iZbp1anc2b2vggfj0i0oovZ data]$ oid2name
All databases:
  Oid Database Name Tablespace
----------------------------------
 16401         newdb pg_default
 12723       postgres pg_default
 12722     template0 pg_default
     1     template1 pg_default
 16393         testdb pg_default
[postgres@iZbp1anc2b2vggfj0i0oovZ data]$ ll base/
total 60
drwx------ 2 postgres postgres 8192 Mar 26 11:26 1
drwx------ 2 postgres postgres 8192 Mar 26 11:26 12722
drwx------ 2 postgres postgres 8192 Mar 29 16:36 12723
drwx------ 2 postgres postgres 8192 Mar 29 16:37 16393
drwx------ 2 postgres postgres 8192 Apr  1 10:00 16401
[postgres@iZbp1anc2b2vggfj0i0oovZ data]$

global:用于存放cluster级别共享的全局表,如pg_database,pg_tablespace表,其文件命名依旧采用oid的数字化格式。

[postgres@iZbp1anc2b2vggfj0i0oovZ data]$ ll global/
total 564
-rw------- 1 postgres postgres  8192 Mar 30 20:42 1213
-rw------- 1 postgres postgres 24576 Mar 26 11:26 1213_fsm
-rw------- 1 postgres postgres  8192 Mar 30 20:42 1213_vm
...
-rw------- 1 postgres postgres  8192 Apr  1 10:05 1262
-rw------- 1 postgres postgres 24576 Mar 26 11:26 1262_fsm
-rw------- 1 postgres postgres  8192 Mar 26 11:26 1262_vm
...
-rw------- 1 postgres postgres  8192 Apr  1 10:30 pg_control
-rw------- 1 postgres postgres   512 Mar 26 11:26 pg_filenode.map
-rw------- 1 postgres postgres 23144 Mar 30 20:50 pg_internal.init
[postgres@iZbp1anc2b2vggfj0i0oovZ data]$

我们可以从数据库中验证这些cluster级别的共享系统表的oid和global下的数字文件名匹配:

postgres=# select oid,relname from pg_class where relname='pg_tablespace';
oid |   relname    
------+---------------
1213 | pg_tablespace
(1 row)

postgres=# select oid,relname from pg_class where relname='pg_database';
oid |   relname  
------+-------------
1262 | pg_database
(1 row)

postgres=#

关于文件名中类似如1213_fsm、1213_vm的文件,表示空闲空间映射文件(free space map),可见性映射文件(visibility map)。后面单独讲解。

pg_commit_ts:事务提交时间戳信息;commit timestamp;

pg_dynshmem:共享内存使用的文件信息;dynamic shared memory;

pg_hba.conf:关于客户端如何访问数据库的配置文件(会单独拎出来讲解);比较重要;host based authentication;

pg_ident.conf:关于外部用户(操作系统用户)和数据库内部用户的映射配置文件,比较少用;ident;

pg_logical:数据库逻辑解码的状态数据;

pg_multixact:存放多事务状态数据,共享锁信息;multi transaction;

pg_notify:数据库配置订阅模式的LISTEN/NOTIFY状态数据;

pg_replslot:数据库复制槽信息;replication slot;

pg_serial:已提交的串行化事务信息;serializable;

pg_snapshots:导出的快照信息,内置pg_export_snapshot()函数导出;

pg_stat:统计子系统收集的统计信息;statistics;

pg_stat_tmp:统计子系统收集的临时统计信息;statistics temporary;

pg_subtrans:子事务的转态数据;sub-transactions;

pg_tblspc:表空间的映射,比较重要,实际存放的是软连接文件,创建表空间之后,会指向实际存放路径;如果没有额外创建表空间,则此路径为空。tablespace;

[postgres@iZbp1anc2b2vggfj0i0oovZ data]$ ll pg_tblspc/
total 0
lrwxrwxrwx 1 postgres postgres 22 Mar 30 20:41 16397 -> /home/postgres/tbs_dir
[postgres@iZbp1anc2b2vggfj0i0oovZ data]$

pg_twophase:两阶段提交事务的状态信息;

PG_VERSION:存放数据库版本信息;

pg_wal:存放数据库的日志文件,非常重要。类似于Oracle的redo log。write ahead log;

[postgres@iZbp1anc2b2vggfj0i0oovZ data]$ ll pg_wal/
total 16384
-rw------- 1 postgres postgres 16777216 Apr 1 10:55 000000010000000000000001
drwx------ 2 postgres postgres       6 Mar 26 11:26 archive_status
[postgres@iZbp1anc2b2vggfj0i0oovZ data]$

pg_xact:事务提交状态数据,用于控制事务的Multi Version Concurrent Contril;PostgreSQL事务有4种状态:IN_PROGRESS,COMMITED,ABORTED,SUB_COMMITTED;

postgresql.auto.conf:存放通过alter sytem命令修改的参数,不建议直接修改该文件;

postgresql.conf:数据库配置的参数文件,非常重要(后面单独拎出来讲)。类似于Oracle的参数文件spfile;

postmaster.opts:记录数据库启动时的选项,options;

[postgres@iZbp1anc2b2vggfj0i0oovZ newdata]$ cat postmaster.opts 
/data/postgres/13.2/bin/postgres "-D" "/data/postgres/13.2/newdata"
[postgres@iZbp1anc2b2vggfj0i0oovZ newdata]$

四 引用和参考

1 FUJITSU关于PostgreSQL内存组件的概述

https://www.postgresql.fastware.com/blog/back-to-basics-with-postgresql-memory-components

2 柃木启修(Hironobu SUZUKI)的The Internals of PostgreSQL

https://www.interdb.jp/pg/pgsql02.html#_2.2.

3 PostgreSQL官方文档

https://www.postgresql.org/docs/current/storage-file-layout.html

4 《PostgreSQL for DBA Architects》–Jayadevan Maymala

一条评论

留言