设计特性
天然集群
PostgreSQL天然集群,如果是单机系统则是单个集簇,相当于一个人组成一个集群的一部分。
PostgreSQL如何管理?是通过一个无符号4个字节的标识进行管理,一个对象就是集群里的一个数据库。
数据库对象和对象符号标识可以通过 pg database和pg classs查询,意味着数据库和对象之间映射。
另外集群在物理磁盘中通过文件目录形式表现,一个目录对应一个数据库,也就是一个base下子目录中有一个目录就是有一个数据库,
值得注意的是表空间表示包含基础目录之外数据目录,这和MySQL区别是表空间含义和存储内容完全不同,在 下面内容会介绍表空间的细节。
通常情况下集群的目录结构如下,可以看到pg_data充当数据库:
一切皆文件
如果观察PostgreSQL的目录结构就能发现他和Linux有相似的地方,就是一切细节都藏在物理文件。
因为数据库充当一个目录,Postgresql会根据数据目录以及配置文件和端口号文件创建实例,其中包含版本号,日志,索引,事务状态等等一切相关信息,对于Postgresql来说都有相关文件进行管理和标识,所有信息类似列好的文件夹,所以可以说Postgresql的底层细节全部展示在数据目录文件当中。
表空间
表空间有点类似基础数据的一个映射,在基础数据中建立映射会按照版本和文件夹命名规则建立对应的表空间映射,用于存储基础数据以外的内容,但是表空间的引入是在9.3版本。
并行查询
并行查询的目的是为了利用现代多核心CPU的特性而逐渐发展的,这种特性被称为并行查询。
如果没有并行查询,对于一些优化空间很小或者优化效益非常小的SQL使用并行查询可以显著的提升查询速度。
并行查询带来的速度提升是显著的,这和使用缓存不同并行查询会让整个SQL从根本上提升数倍(当然依然取决于SQL质量)。并行查询更大到优点是:那些访问大量数据但只返回其中少数行给用户的查询最能从并行查询中获益,这种场景在日常生活中经常见到。
并行查询的内容会放到后续的内容中进行记录。
数据页格式
PostgreSQL存储数据页的内容被称为堆表,一个堆表中数据页为固定为8kb,注意不管是索引还是数据还是其他类型在Postgresql数据库当中都看作堆表,都是固定大小8KB,当然可以在配置文件里面扩大单个数据页的大小,但是通常只有做OLAP业务的时候有可能用到,默认使用8KB的设置即可。
在堆表中数据分为三部分,文件头,行指针,和元组,元组就是真实数据的存储位置,空闲部分表示未使用空间,下面是对应的内容:
文件头:大小为24b,存储页面元数据,定义为page_header_data,主要成员变量如下:
- pd Lsn:写日志(XLog)的相关lsn号码,可以看作是MVCC的记录ID。
- pd checksum:校验和,用于校验数据页的完整性,需要注意校验和在9.x版本才加入此参数,为了兼容过去的版本,此参数有可能存放其他的内容
- pd lower/pd upper :一个指向行指针尾部,另一个指向最新堆元组的起始位置。通过首尾指针标记当前未使用空间,数据未使用空间等情况。
- 标记位:主要用于当前记录进行特殊标记。
- pd speacial 特殊标记,普通数据页指向页尾,而索引文件指向特殊空间,也就是存放索引等信息的空间。
- version:页面大小和布局版本信息
- xid:最老元组的xid也就是元组的第一个数据位置
- 行指针数据组,实例变量(注意这个参数不会存磁盘)
缓冲页的项目指针叫做行指针:
行指针分为三部分,存储为无符号类型
- 15位偏移量,
- 15位字节长度
- 2位行指针标记
行指针的这种设计决定了数据页最大只能是32kb
另外大于2kb的堆元组会使用超大属性存储技术来单独存储数据,这是啥,有点类似MySQL的blob页。
元组读写方式
写入方式比较好理解,就是在行指针后面插入新的数据,以及在末端元组加入新数据,把新的行指针指向新加入元组,同时把数据堆表文件内部数据页的lower个upper往后和往前挪动,标记未使用空间。
读取方式分两种:
- 第一种是通过行指针遍历,o1的查找速度
- 第二种是BTree扫描,键存储索引值,值存储的是堆元组的tid,这个属性记录堆元组偏移量和长度信息,可以直接通过扫描堆元组找到,可以看到也是o1的速度
PostgreSQL扫描方式
- tid扫描
- 仅索引扫描
- 位图扫描
进程架构
PostgreSQL是一款多进程协同配合的架构数据库,主要工作工作进程分为下面几种:
- 服务器进程:运行PostgreSQL服务的主要进程
- 后端进程:主要负责用户crud操作请求
- 后台进程:负责各种维护服务端正常工作的进程以及负责各种数据库任务,比如清理缓存数据
- 复制进程:负责流复制
- 后台工作进程:用户可以自己编写进程,同时也是9.3引入的新功能
一共有2个后端进程,7个后台进程和2个客户端进程
服务器进程
也被叫做master进程,服务器进程主要负责整个PostgreSQL的服务启动,以及负责启动各种后端和其他工作进程的任务,如果有需要会启动复制进程和用户自己编写的进程
后端进程
后端进程需要依靠PostgreSQL启动,后端进程会在用户发出查询请求的时候被启动负责处理用户查询请求。
但是因为pgsql没有天生自带的连接池功能,所以需要配合连接池中间件减少频繁创建PostgreSQL后端进程的开销。
后台进程
后台进程的种类和工作非常丰富 要全部列举是不现实的,下面列举一些常见的后台进程
- 后台写入进程:负责把脏页刷新回写到磁盘中
- 检查点进程:负责处理检查点
- 周期清理工作进程,更准确的是定时通知主进程创建工作清理进程工具
- wal刷新进程:wal是啥后面会提到,主要工作是吧wal刷新同步
- 日志收集进程:负责把错误日志收入到一处方便dba维护
- 归档进场:其实还是把日志进行归档整理的一个进程。
问题:对于SQL的其他操作通过Postgresql的服务器完成相关工作。
检查是什么?检查点检查的是当前系统进程的状态,比如当前内存的脏数据页的大小,以及当前的系统运行状态等情况。
可以看到PostgreSQL的系统设计是通过小组件搭配的方式,每一个进程各司其职,使得整个工作配合的井井有条。
PostgreSQL内存结构
内存结构分为本地内存和进程共享内存。
本地内存针对每一个后端进程使用,没一个后端进程拥有独立的本地内存,这个区域会分为几个子区域,并且有的固定不变,有的可以改变,而共享的分配区域分为几个固定大小的子区域:
- 访问控制子区域
- 后台进程使用子区域
- 事务处理子区域,比如2PC的特性支持。