所谓的内存结构,就是Postgresql在运行时的内存构成,即有多少个进程,每个进程的作用是什么。我们先来看一张PG服务器的运行时内存结构图:
接下来,我们分别介绍了Postgresql各个进程的作用。
postmaster进程是PG数据库最主要的管理进程,它的作用主要有两个:
/usr/pgsql-11/bin/postmaster -D $PGDATA
如果是在centos7+下使用yum安装的postgresql,那么postgresql的启停会托管给systemctl,我们在/usr/lib/systemd/system/postgresql-11.service文件中就可以看到配置的ExecStart就是使用的上述命令:
实际上,postmaster 命令只是postgres命令的一个软链接,可以看到postmaster 的目标命令是postgres。如下所示,postmaster命令只是同目录postgres命令的一个软连接
[postgres@VM-115-39-centos /usr/pgsql-11/bin]ls -l postmaster lrwxrwxrwx 1 root root 8 Jul 25 2020 postmaster -> postgres
Postgresql针对每个客户端的链接都会fork一个子进程,如下所示,圈出部分就是客户端的一个远程链接而创建的postgres进程,所以Postgresql在处理客户端链接时成本还是很高的。
bgwriter进程是将共享内存中的脏数据块写入到磁盘上的进程。这里,先解释下什么是脏数据块。当向数据库中插入或者更新数据时,往往不会马上将这些更新的数据写入到磁盘,而是直接在内存中进行更新,这样做主要是为了提高更新或者插入的效率。但是这也就引入了一个问题——内存中的数据和磁盘中已经持久化的数据是不一致的,我们就把这部分内存中的数据称为脏块。bgwriter进程的作用就是按照一定的周期,将脏块的数据写入到磁盘。这个更新的频率可以通过postgresql.conf配置文件进行配置,过高或者过低的频率都有问题,比如说过高的话,磁盘IO过于频繁,性能会降低;过低时,有可能造成内存不足,当有新的数据要写入或者要查询脏块中的数据时,就需要将数据先写入到内存中,必然会造成更新或者查询更慢。所以我们进行配置时要找到一个合适的频率。
postgresql中的wal日志也叫预写日志,和oracle中的redo日志类似,执行数据更新时,先要将更新记录记录到磁盘上,主要是为了防止在数据库服务器突然宕机时,内存中的脏数据丢失,可以根据wal日志进行数据的恢复。wal日志的保存目录是在$PGDATA/pg_wal目录下,可以看到文件名逐渐递增,而且每个文件的大小都是16M,总的大小可配置,当达到了最大的值,新的Wal日志会覆盖旧的:
logger进程可以收集包括postmaster进程在内的所有进程的stderr日志,这里需要注意是错误日志哦,并不是所有的日志它都会收集。它支持通过postgresql.conf文件修改日志相关的参数,可以设置最大值并循环覆盖,日志的默认目录在$PGDATA/logs/,像我本机默认的日志记录如下,每天一个日志文件,按照星期存储:
查看某个日志的内容,发现都是一些错误的信息:
和oracle类似,postgresql也有检查点进程checkpoint,检查点进程的主要作用就是,辅助bgwriter进行内存中脏数据块的写入。实际上,当bgwriter向磁盘中写入脏块时,是通过checkpoint进程实际写入的。bgwriter在扫描出脏块后,会发起写入的request,而checkpoint进程会读取这些request,将脏块批量写入磁盘,并且会记录已经写入后的脏块,从而保证下次只会写入新的脏块。
该进程主要是两个作用:
(1)PG数据库中DELET操作,原来的数据并不会被立即删除。即使是Update的数据,也不会在旧的数据行上进行更新,而是新生成一行数据,将原来的数据行标记为删除状态。这就是为了PG数据库的共享锁和独占锁机制,只有在其它事务没有在读取这些旧数据时,它们才会被删除。而autovacuum进程的作用就是自动清理这些被标记为删除状态的数据。
(2)负责统计信息的收集,用于查询优化时的代价的估算。
该进程的主要作用就是数据的统计收集,比如在一个表上进行了多少次的插入、更新和删除操作,磁盘块的读写次数、行的读写次数等等。
用于逻辑复制,订阅者通过 logical replication launcher进程向发布者订阅表的更新并获取更新。
归档进程,所谓归档,就是在旧的WAL日志在覆盖前把其备份出来,Postgresql支持一种PITR(Point-In-Time-Recovery)技术,就是在数据库进行一次全量备份之后,该技术会将备份时间点之后的WAL日志归档备份,这样使用全量备份再加上WAL日志,就可以将数据库恢复到任意的时间点了。但是需要注意的是Postgresql默认的配置是不开发归档功能的,如果需要开放,要在postgresql.conf文件中设置archive_mode = on
主要作为数据缓冲区,提高读写性能,作为WAL日志和Commit日志的缓冲区等等。
可以理解为一个数据库的容器,可以存放多个数据库实例(create database)。Postgresql的数据库是一个多租户的概念,租户之间互相隔离。
这个不用多解释了,使用create database可以创建数据库。
模式是相同数据库下逻辑隔离的基本单位,可以通过create schema创建不同的模式,其实有点类似与oracle中的用户,oracle中不同用户可以创建不同的表,表名可以重复,其它用户访问别的用户的表时需要带上用户名,这样就实现了用户间的数据隔离,而Postgresql中使用模式达到这种数据隔离的目的。
test=# create schema myschema; CREATE SCHEMA test=# create table myschema.mytable(id INT,name varchar); CREATE TABLE test=# \d+ myschema.mytable Table "myschema.mytable" Column | Type | Collation | Nullable | Default | Storage | Stats target | Description --------+-------------------+-----------+----------+---------+----------+--------------+------------- id | integer | | | | plain | | name | character varying | | | | extended | |
[postgres@VM-115-39-centos ~/11/data]cat postmaster.opts /usr/pgsql-11/bin/postgres "-D" "/var/lib/pgsql/11/data/"
以上是主要的文件,接下来是几个主要的子目录
数据库的OID:
postgres=# select oid,datname from pg_database; oid | datname -------+------------------------- 13881 | postgres 1 | template1 13880 | template0 16385 | stock_analysis_data 17724 | stock_test_activiti 18121 | stock_analysis_activiti 18621 | test 18622 | test1 18623 | test2
base目录下的子目录:
[postgres@VM-115-39-centos ~/11/data]ls base/ 13880 13881 16385 17724 18121 18621 18622 18623 pgsql_tmp
接下来,我们思考一个问题:数据库中每个表里面的数据都是存到磁盘文件中的,那么这些文件的存储位置在哪呢?要想弄清楚这个问题,我们需要了解一个概念——relfilenode,当我们创建一个表或者索引时,Postgresql会为每个表和索引都分配一个relfilenode(实际上就是一个int类型的编号),可以通过pg_class查询到某个表的relfilenode。比如查询表fund的relfilenode:
stock_analysis_data=# select relnamespace,relname,relfilenode from pg_class where relname='fund'; 2200 | fund | 17586
fund是数据库stock_analysis_data中的一个表,通过上面我们从pg_database查询数据库的OID得知,stock_analysis_data的OID是16385 ,而stock_analysis_data用的是默认的表空间,从上面的查询中可以看到,fund表的relfilenode为17586。一张表的数据文件目录为:
<默认的表空间目录>/<数据库 oid>/<relfilenode>[.顺序号]
其中,顺序号从1开始,当表或索引的大小超过1G时,顺序号加1,所以fund表对应的文件路径如下:
/var/lib/pgsql/11/data/base/16385/17586