《PostgreSQL数据库内核分析》之PostgreSQL体系结构(二)

举报
毛竹 发表于 2024/02/02 08:50:41 2024/02/02
【摘要】 2.1 PostgreSQL体系结构2.2 系统表数据字典不仅存储各种对象的描述信息,而且存储系统管理所需的各种对象的细节信息数据字典包含数据库系统中的所有对象及其属性的描述信息、对象之间关系的描述信息、对象属性的自然语言定义以及数据字典变化的历史(即数据库的状态信息)数据字典是关系数据库系统管理控制信息的核心,在PostgreSQL数据库系统中,系统表扮演者数据字典的角色。内存中建立了共享...

2.1 PostgreSQL体系结构

2.2 系统表

数据字典不仅存储各种对象的描述信息,而且存储系统管理所需的各种对象的细节信息
数据字典包含数据库系统中的所有对象及其属性的描述信息、对象之间关系的描述信息、对象属性的自然语言定义以及数据字典变化的历史(即数据库的状态信息)
数据字典是关系数据库系统管理控制信息的核心,在PostgreSQL数据库系统中,系统表扮演者数据字典的角色。
内存中建立了共享的系统表CACHE,使用Hash函数和Hash表提高查询效率
系统表功能的实现代码包含系统表定义文件和系统表绑定函数实现文件。分别位于如下位置:
1.src/include/catalog pg_xxx开头的.h文件
indexing.h 定义了所有的系统表索引
toasting.h 定义了所有系统表的TOAST表(TOAST表用于存储普通表中的超长属性值)
2.src/backend/catalog
indexing.c 四个操作系统表索引的函数
toasting.c 四个操作系统表的TOAST表的函数

2.2.1 主要系统表功能及依赖关系

1.pg_namespace
存储命名空间 每个名字空间有独立的关系、类型等集合,但并不会相互冲突。PG的名字空间层次是:数据库、模式、表、属性
PostgreSQL会按以下名字空间顺序进行搜索:
1.特殊名字空间(special),仅用于创建模式
2.临时表的名字空间(TEMP)
3.系统表的名字空间
1.pg_namesoace 每个元组都对应一个名字空间,每一个名字空间都被分配一个OID作为唯一标识,并且存储在对应元组的隐藏属性中,
 
属性名
数据类型 注释
nspname NameData(长度为64的字符数组) 用于存放命名空间的名称
nspower Oid 用于表示该命名空间的所有者,由于系统中每一个用户也会分配一个Oid作为唯一标识,因此这里nspower中只存储所有者的Oid
nspacl aclitem类型的边长数组 其中存放对于该名字空间的访问权限列表
​2.pg_tablespace 存储表空间,将表放置在不同的表空间有助于实施磁盘文件布局。pg_tablespace在整个数据集簇里只有一份,同一个数据集簇内的所有数据库共享一个pg_tablespace表,而不是每个数据库都有自己的pg_tablespace​
 
属性名
数据类型 注释
spcname NameData 存储表空间名称
spcowner Oid 表示该表空间的所有者,通常为创建表空间的用户
spclocation Text(实际是一种变长的字符串数据类型) 用于存储表空间的物理位置(操作系统目录路径)
spcacl aclitem类型的变长数组 存放对于该表空间的访问控制列表
3.pg_database 存放了当前数据集簇中数据库的信息,也是一个在整个集簇范围内共享的系统表。该表中每一个元组就表示聚簇中的一个数据库,每一个数据库都被分配一个OID作为唯一标识,并且存储在对应元组的隐藏属性中
4.pg_class pg_class存储表与表类似结构的数据库对象信息,包含索引、序列、复合数据类型、TOAST表等。每一个对象都在pg_class中表示一个元组,并且每一个对象都会被分配一个OID作为唯一标识,该OID作为该元组的一个隐藏属性存储。
5.pg_type 存储数据类型信息。基本数据类型和枚举类型由CREATE TYPE创建,域类型由CREATE DOMAIN创建,复合数据类型在表创建时自动创建。
6.pg_attribute 存储表的属性信息,对于数据库中表的每个属性都有一个元组。
7.pg_index 存储索引的具体信息
 
关键系统表之间的相互依赖关系

2.2.2 系统视图

内置视图,初始化数据库集簇时读取脚本创建。系统视图提供了查询系统表和访问数据库内部状态的方法。
视图名
用途
pg_cursors 打开的游标
pg_group 数据库用户的组
pg_indexes 索引
pg_locks 当前持有的锁
pg_prepared_statements 预备语句
pg_prepared_xacts 预备事务
pg_roles 数据库角色
pg_rules 规则
pg_settings 参数设置
pg_shadow 数据库用户
pg_stats 规划期统计
pg_tables
pg_timezone_abbrevs 时区缩写
pg_timezone_names 时区名
pg_user 数据库用户(无口令属性)
pg_views 视图

​系统表是PG运行控制信息的来源,是数据库系统的核心组成部分。虽然用户可以操作,但为维护系统表信息的一致性,系统表将由系统统一维护。PG安装后,需要先进行初始化数据库操作(initdb),生成模板数据库和相应的目录、文件信息,系统表即在此阶段生成。而用户数据库机器系统表都是从模板数据库进行复制生成的。

2.3数据聚簇

pg安装成功后,首先必须使用initdb程序初始化磁盘上的数据存储区,即数据集簇,由PG管理的用户数据库及系统数据库总称为数据集簇。PG所有的数据都存储在其数据目录里,这个数据目录通常会用环境变量PGDATA来引用。
OID通常从1开始,在初始化数据集簇时,会先将一部分OID分配给系统表、系统表元组、系统表上的索引等数据库对象,这一部分OID可以在系统表所对应的头文件中找到。系统运行时可分配的OID资源实际是从16384开始的。
初始化数据集簇包括创建包含数据库系统所有数据的数据目录、创建共享的系统表、创建其他的配置文件和控制文件,并创建三个数据库:模板数据库template1和template0,默认的用户数据postgres。以后创建的数据库都是从template1拷贝过来的。template0和postgres都是通过拷贝template1创建的。
对于某个具体的数据库,在PGDATA/base里都对应一个子目录,子目录的名字是该数据库在系统表pg_database里的OID。
每个表和索引都存储在其所属数据库目录下的独立文件里,以该表或该索引的filenode号命名,该号码记录在该表或索引在系统表pg_class中对应的relfilenode属性中。
表或索引创建超过1G之后,会被分裂成多个1G大小的段。第一段的文件名和filenode相同,随后的段命名为filenode.1,filenode.2....这样的策略避免了某些有文件大小限制的平台上可能出现的问题。
表空间 PGDATA/tblspc
默认创建的表空间 pg_default和pg_global -> PGDATA/base和PGDATA/global
数据集簇:pg先使用initdb程序初始化磁盘上的数据存储区,即数据集簇。由pg管理的用户数据库及系统数据库总称为数据集簇。数据目录通常会用环境变量PGDATA来引用。
OID无符号整数,从1开始,但初始化数据集簇时,会先将一部分OID给系统表、系统元祖、系统表上的索引等数据库对象。在系统运行时分配的OID资源实际是从16384开始。
initdb负责创建数据库目录、系统表、模块数据库。

2.3.1initdb

initdb是使用PG之前用于初始化数据集簇的程序,负责创建数据库目录、系统表、模板数据库。
initdb把用户指定的选项转换成对应的参数,通过外部程序调用的方式执行postgres程序。postgres程序在这种方式下将进入bootstrap模式创建数据集簇,并读取后端接口postgres.bki文件来创建模板数据库。

2.3.2postgres.bki

编译在过程中由/src/backend/catalog目录下的脚本程序 genbki.sh读取/src/include/catalog目录下的以.h结尾的系统表定义文件(包括系统表索引和TOAST表定义文件)创建,并且通常存放在安装树的share子目录下。在pg_*.h的头文件中包含了如下内容的定义:
1.定义CATALOG宏:用于以统一的模式去定义系统表的结构以及用以描述系统表的数据结构,如系统表pg_class的定义通过CATALOG(pg_class,1259)来表现。
2.通过宏DATA(x)和DESCR(x)来定义insert操作,这样的insert操作可能会有多个,用于定义系统表中的初始数据。

2.3.3intidb的执行过程

执行initdb程序时,将从initdb.c文件中的main函数来开始执行

2.3.4系统数据库

三个系统数据库template1、template0和postgres

2.4PostgreSQL进程结构

守护进程Postmaster 负责整个系统的启动和关闭,监听并接受客户端的连接请求,为其分配服务进程postgres
服务进程Postgres 接受并执行客户端发送的命令,在底层模块(如存储、事务管理、索引等)之上调用各个主要的功能模块(如编译器、优化器、执行器等),完成客户端的各种数据库操作
Unix或Linux系统下,Postmaster仅仅是一个符号链接;
Windows中,Postmaster是Postgres的一个拷贝,因此pg系统几乎是所有的核心功能都是由Postgres程序完成
Postmaster(单用户模式下的Postgres进程)除了为用户链接请求分配后台Postgres服务进程外,还将启动相关的后台辅助进程。
守护进程Postmaster完成初始化 -> 创建接受用户请求的监听端口,顺序启动如下系统辅助进程:
SysLogger(系统日志进程)、PgStat(统计数据收集进程)、AutoVacuum(系统自动清理进程),在守护进程Postmaster进入循环监听中时启动如下进程:BgWriter(后台写进程)、WalWriter(预写式日志写进程)、PgArch(预写式日志归档进程)
PG采用C/S模式,系统为每个客户端分配一个服务进程。
Postmaster总是监听用户连接请求并为用户分配服务进程Postgres,而Postgres则负责为客户端执行各种命令。

2.5守护进程Postmaster

多用户模式下一个数据库实例由数据库服务器守护到进程Postmaster来管理。是一个运行在服务器上的总控进程,负责整个系统的启动和关闭,并且在服务进程出现错误时完成系统的恢复。
管理数据库文件、监听并接受来自客户端的连接请求,并为客户端连接请求fork一个Postgres服务进程,代表客户端在接受来自客户端的连接请求。同时Postmaster还管理与数据库运行相关的辅助进程。用户可使用postmaster、postgres或pg_ctl命令启动Postmaster。

2.5.1初始化内存上下文

7.1开始,实现了新的内存管理机制,使得运行时大多数内存分配操作在各种语义的内存上下文(MemoryContext)中运行。内存上下文释放时将会释放在其中分配的所有内存,避免内存泄漏。
MemoryContextInit(TopMemoryContext、ErrorContext) -> AllocSerContextCreate(以TopMemoryContext为根节点创建PostmasterContext) -> 将全局指针CurrentMemoryContext指向PostmasterContext
TopMemoryContext 在TopMemoryContext中分配的内存直到系统退出时才会释放
ErrorContext 错误恢复处理的永久性内存环境,恢复完毕则重设
PostmasterContext Postmaster正常工作的内存环境,由它通过fork函数产生的子进程将会删除这个环境

2.5.2配置参数

GUC(Grand Unified Configuration)模块实现了多种数据类型(boolean、int、float、string四种)
六种类型(通过枚举类型GucContext定义),并且只能在合适的环境下进行配置:
注册完信号处理函数后,将逐行读取data目录下的pg_hba.conf和pg_ident.conf两个配置文件的内容到链表变量中,以用于控制客户端认证。
pg_hba.conf 基于主机认证的配置文件
pg_ident.conf 常用格式为每行一个记录

2.5.3循环等待客户连接请求

PostgreSQL系统采用C/S模式,为每个客户端分配一个后台服务进程Postgres。一次PostgreSQL会话由下列进程组成:
1.服务进程叫Postgres,接受来自客户端应用与数据库连接,并且代表客户端在数据库上执行操作。
2.客户端进程 运行在客户端与Postmaster或Postgres进程交互的进程,比如在客户端运行的psql进程。

2.6辅助进程

PG进程中为每个辅助进程设置了一个全局变量来标识该进程的进程号,分别为SysLoggerPID、BgWriterPID、WalWriterPID、AutoVacPID、PgArchPID、PgStatPID 0表示为启动

2.6.1SysLogger系统日志进程

可设置日志文件的大小,SysLogger会在日志文件达到指定的大小时关闭当前日志文件,产生新的日志文件。

2.6.2BgWriter后台写进程

BgWriter是PG中在后台将脏页写出到磁盘的辅助进程,引入该进程主要为达到如下两个目的:
1.定期写出缓冲区中的部分脏页到磁盘中,为缓冲区腾出空间,可降低查询处理被阻塞的可能性;
2.定期作检查点时需要把所有脏页写出到磁盘,通过BgWriter预先写出一些脏页,可以减少设置检查点时要进行的IO操作,使系统的IO负载趋向平稳。避免了其他进程在需要读入新的页面到共享缓冲区时,不得不将之前修改过的页面写出到磁盘的操作。 8.0以后的特性

2.6.3WalWriter预写式日志写进程

Write Ahead Log / Xlog
对数据文件的修改必须之恶能发生在这些修改已经记录到日志之后 8.3之后的特性
WAL日志文件存放在数据集簇中的pg_xlog目录中
每个段16MB,并分割成若干页,每页8KB。日志记录头格式在xlog.h里描述,内容决定于它记录的事件的类型。一个段文件的名字由24个十六进制字符组成,分为三部分,每个部分由8个十六进制组成。
WAL缓冲区和控制结构在共享内存中,用轻量的锁来保护,对共享内存的需求由缓存区数量决定,默认的WAL缓冲区大小是8个8KB的缓冲区(即64KB)

2.6.4PgArch预写式日志归档进程

8.x提出了PITR(Point-In-Time-Recovery)技术,支持将数据库恢复到其运行历史中任意一个有记录的时间点。
除了WalWriter外,PITR的另一个重要的基础就是对WAL文件的归档功能。PgArch辅助进程的目标就是对WAL日志在磁盘上的存储形式(Xlog文件)进行归档备份。

2.6.5AutoVacuum系统自动清理进程

自动vacuum和analyze,回收被标记为删除状态记录的空间,更新表的统计信息
2.5.6PgStat统计数据收集进程
Pgstat辅助进程是PostgreSQL数据库系统的统计信息收集器,专门负责收集数据库系统运行中的统计信息,如在一个表和索引上进行多少此插入和更新操作、磁盘块的数量和元组的数量、每个表上最近一次执行清理和分析操作的时间,以及统计每个用户自定义函数嗲偶哦那个执行的时间等。

2.7服务进程Postgres

用户命令分为两种:1.查询命令,插入、删除、更新和选择;2.非查询命令,如创建/删除表、视图、索引等命令。服务进程Postgres根据不同的命令类型选择不同的策略进行处理
Postgres进程主要源码位于src/backend/tcop文件夹下,主要文件包括:
1.服务进程源代码文件postgres.c,是Postgres的入口文件,负责管理查询的整体流程
2.对于查询命令进行处理的源代码文件pquery.c,执行各种非查询命令
3.对于非查询命令进行处理的源代码文件utility.c,执行各种非查询命令
4.dest.c的代码主要处理Postgres和远端客户的一些消息通信操作,并负责返回命令的执行结果
1.将参数设置为默认值
2.根据命令行参数配置参数
3.读配置文件重新设置参数
单用户模式下这三步和Postmaster完全相同。
命令行方式的参数设置优先级是高于缺省设置的,将会覆盖缺省设置。命令行参数有很多,和Postmaster一样,在Postgres中仍使用while+switch控制结构读入参数并进行配置。
之后Postgres还将从Port结构得到客户端传递的GUC选项,然后根据GUC选项的具体请跨u那个调用SetConfigOption进行设置。

2.7.1设置信号处理和信号屏蔽

Postgres中信号的注册方式和Postmaster类似,因此这里只对Postgres中处理的信号及其意义进行介绍,其中有几个重要的信号处理函数意义如下:
1.SIGHupHandler 当配置文件发生改变时产生此信号。服务进程在收到SIGHUP信号时重读配置文件postgresql.conf,并重新装载pg_hba.conf文件
2.StatementCancelHandler 收到SIGINT信号后调用此函数,终止正在进行的查询操作。
3.die 函数处理SIGTERM信号,用来终止当前的服务
4.quickdie 处理SIGQUIT信号,首先屏蔽其他信号,然后结束正在进行的工作并退出。
5.handle_sig_alarrm 该函数处理SIGALRM信号,由进程等待锁的时间超时引发的。如果存在死锁,就将自己从锁等待队列中退出,唤醒自己并在PROC结构中将错误类型置为STATUS_ERROR。

2.7.2初始化Postgres的运行环境

基本参数和信号量处理函数初始化完成后,将进行Postgres进程运行环境的初始化工作。Postgres进程首先会检查DataDir变量,确保给定的DataDir字符串是一个格式正确的数据目录路径。在多用户模式下,此工作由Postmaster进程完成。在有效数据目录路径下,检查PG_VERSION文件中的版本信息是否与当前版本的程序兼容。在版本兼容的前提下,将当前工作目录转到DataDir字符串表示的目录,以方便Postmaster进程及其后台进程使用相对路径访问数据目录。

2.7.3创建内存上下文并设置查询取消跳跃点

Postgres首先创建一个名为MessageContext的内存上下文,该内存上下文用于存储从前端发送过来的消息中的查询命令,以及在查询过程中产生的中间数据(如在简单查询模式时产生的分析树和计划树),每当PostgresMain进行下一次循环(即进行新的查询)时该内存上下文将被重设,即所有经过MessageContext分配的内存块将被释放。
创建完成后将调用sigsetjmp(系统调用函数)设置跳跃点,当客户端取消一次查询请求或发生错误时,将通过调用siglongjmp利用全局变量PG_exception_stack(该变量是指向跳跃点的指针)从这个点退出当前事务然后重新开始查询,在错误恢复期间不允许被中断,也不接受任何客户端取消查询的请求(正在取消)。
PostgreSQL使用的这种错误恢复的方式是C语言程序中进行错误处理的一种常见方式。

2.7.4循环等待查询处理

完成上述工作后,Postgres可真正接受客户端查询并处理,系统开始运行。
1.共享缓冲区PqSendBuffer: 8192字节的输出缓冲区,用来接收服务器发送给客户端的消息。
全局变量PqSendPointer是一个字符指针,表示在PqSendBuffer缓冲区中在此地址之前的消息正在发送给客户端,以后新加入的消息放在PqSendPointer所指向的位置。
2.共享缓冲区PqRecvBuffer:8192  接收客户端发送给服务器的消息。

2.7.5简单查询的执行流程

DML命令,系统调用exec_simple_query函数来执行简单查询,流程如下 ​

1.编译器:主流程的第一个模块。扫描用户输入的字符串形式的命令,检查其合法性,并将其转换为Postgres定义的内部数据结构。Postgres为每一条SQL命令都定义了相应的C语言结构体,用来存储命令中的各种参数。编译器是利用著名的lex和yacc工具编写,其入口为pg_parse_query函数。代码位于src/backend/paser目录下的scan.l和gram.y
2.分析器: 接收编译器传递过来的各种命令数据结构(语法树),对它们进行相应的处理,最终转换为统一的数据结构Query。如果是查询命令,在生成Query后进行规则重写(rewrite)。重写部分的入口是QueryRewrite函数,代码位于/src/backend/rewrite目录下。分析器的入口是parse_analyze函数,其代码位于/src/backend/paser目录下。
3.优化器: 接收分析器输出的Query结构体,进行优化处理 后,输出执行器可以执行的计划(Plan)。一个特定的SQL查询可以以多种不同的方式执行,优化器将检查每个可能的查询计划,最终选择运行最快的查询计划。优化器的入口是pg_plan_quey函数,代码位于/src/backend/optimizer目录下
4.执行器: 执行非查询命令的入口函数是ProcessUtility,代码位于/src/backend/tcop/utility.c 该函数的主题结构式一个switch语句,根据输入的命令类型调用相应的执行函数。执行查询命令的入口函数式ProcessQuery,代码主要位于/src/backend/executor目录下。
Posgres进程在系统中扮演者一个工作执行者的角色,在单用户或多用户模式下,客户端请求通过认证后将直接与服务进程Postgres进程通信,而无须守护进程干预,只在客户端对应的后台进程出问题时由守护进程执行容错恢复工作。Postgres和用户进行交互,执行客户端提交的查询请求和命令,并将执行结果通过网络饭就给用户。Postgres后台进程的运行即实现了PostgreSQL的多任务并发执行。
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。