PG内核解读-第2节PostgreSQL体系结构
一、PostgreSQL系统表
系统表又叫database catalog。数据库实例的数据库目录由元数据组成,其中存储了数据库对象的定义。SQL标准指定了访问目录的统一方法,称为INFORMATION_SCHEMA。
在关系数据库中,信息模式(information_schema)是一组ANSI标准的只读视图,提供有关数据库中所有表、视图、列和过程的信息。它可以作为某些数据库,通过非标准命令提供信息的来源。例如MySQL的SHOw命令;Oracle的SQL*Plus的DESCRIBE命令;PostgreSQL的默认命令行程序。
在设计一个系统时,最先需要设计的数据结构是系统表。大多数系统表都是在数据库创建的过程中从模版数据库中拷贝过来的,因此都是数据库相关的。少数表是在整个安装中物理上所有数据库共享的。
用户可以输入:‘\d+pg_class’查看pg_class定义,pg_class记录了所有表、视图信息。
用户可以通过PG手册了解每个系统表的定义。相关链接如下:
https://www.postgresql.org/docs/current/catalogs-overview.html
https://www.postgresql.org/docs/current/catalog-pg-class.html
https://www.postgresql.org/docs/current/views-overview.html
用户也可以通过SQL语句,查看和筛选namespace系统表中的信息。
除此之外,用户可以通过查看database系统表信息
用户如何使用系统表呢?首先,用户可以查看系统中表的相关信息,例如select*from pg_class。
其次,用户可以对表进行调优,设置库命中率,表命中率以及调整表的大小。
除此之外,PG提供了一种跟踪服务器,能够执行所有SQL语句的规划和统计信息,即pg_stat_statements。
用户可以通过查询pg_stat_statements视图,得到数据库资源开销的统计信息。SQL语句中的一些过滤条件在pg_stat_statements中,会被替换成变量,从而减少重复显示的问题。
PostgreSQL的系统表的相关文件目录如下,大家可以根据自己的需要进行查找:
/home/michael.yw/postgres/src/backend/catalog
OID是PostgreSQL内部用于标识数据库对象的标识符,包括数据表,视图,存储过程等。OID在系统表中通常是作为隐藏列存在,在PostgreSQL数据库实例的范围内进行统一分配。
由于OID是系统表的隐藏列,所以用户在查看系统表中数据库对象的OID时,必须在SELECT语句中显式指定。
当用户有了一个系统表,在进程启动时,需要建立SysCache(CatCache)和RelCache缓存系统表的信息。
RelCache、CatCache和PlanCache加速系统访问,减少占用的资源。其中,relcache.c中存放了最近访问过的普通表缓存,RelCache使用Hash表进行管理。
如上图所示,用户可以通过命令:select*from pg_class,查询系统表的相关信息。
用户可以通过命令,查询系统表中的某一张表,用户可看到文件对应的编号。相关命令如下:
select oid,relname,relfilenode from pg_class where relname='t1'
如上图所示,系统的库命中率达到了0.998。当用户需要查询库命中率,需要执行如下命令:
select blks_read,blks_hit,
blks_hit::numeric/(blks_read+blks_hit)as ratio
from pg_stat_database
where datname='postgres'
当用户需要查询表命中率时,相关命令如下:
SELECT relname,heap_blks_read,heap_blks_hit,
round(heap_blks_hit::numeric/(heap_blks_hit+heap_blks_read),3)
FROM pg_statio_user_tables
WHERE heap_blks_read>0
ORDER BY 2 DESC
当用户需要查询表的大小时,需要执行以下命令:
select relname,pg_size_pretty(pg_relation_size(relid))from pg_stat_user_tables where schemaname='public'order by pg_relation_size(relid)desc
接下来,查询前五名最耗IO的SQL,相关命令如下:
select*from pg_stat_statements order by(blk_read_time+blk_write_time)desc limit 5
接下来,查询前五名单次调用最耗时的SQL,相关命令如下:
select userid::regrole,dbid,query from pg_stat_statements order by mean_time desc limit 5
接下来,如上图所示在catlog的目录下,查看系统表的相关文件,相关目录如下:/home/michael.yw/postgres/src/backend/catalog
接下来,查看SysCache(CatCache)和RelCache。
二、PostgreSQL初始化、启动、查询流程
在PostgreSQL初始化过程中,用户在编译阶段,通过genbki.pl获得postgres.bki。然后通过InitDB创建目录,bootstrap_templatel-利用BKI脚本在bootstrap模式生成templatel数据库。make_templateo-拷贝templatel生成template0,template0不接收连接,不可修改。
PostgreSQL有三种启动模式,分别是Postmaster、bootstrap和single通过这三种方法,用户创建TopMemoryContext,信号处理函数、加载GUC参数、监听端口。
当用户收到新用户请求,建立新的backend处理请求,共享内存,开始辅助进程启动。
上图是PostgreSQL的执行流程。Client App通过Interface Library,连到Post master进行监听,创建一个Postgres Server进行服务,收到SQL之后,进行解析,生成Parse tree,判断类型和执行路径之后,进行优化。
路径优化之后,形成plan tree并在执行器里执行,然后用不同的方法提取数据,将最终结果返回给客户。
在初始化流程中,PostgreSQL在编译阶段,检查并生成OID。通过命令setap.pgdata,以PGIDATA环填变量生成数捆目录路径。
通过initialize_data_directory初始化数据目录。用命令create_data_directory,创建数据目录。通过命令setup_confg,生成配置文件并修改。命令bootstrap_template,利用BKI脚本在bootstrap模式生成templatel数据库。
最终,setup_sysviews基于system_views_file,生成sysview。
setup_schema基于information_schema.sql"生成系统表。
make_templateo-拷贝templatel生成template0,template0不接收连接,不可修改。
make_postgres-拷贝templatel生成postgres库。
接下来,演示PostgreSQL的启动流程。当PostgreSQL通过Postmaster启动时,通过AllocSetContextCreate(TopMemoryContext),创建TopMemoryContext。然后,通过pqsignal_pm(SIGHUP,SIGHUP_handler),设置信号处理函数;用户通过InitializeGUCOption,加载GUC参数。
用户通过StreamServerPort建立监听端口,然后,完成创建共享内存及互斥变量。通过命令SysLogger_Start,启动日志进程。
当用户完成启动Startup回放进程,启动注册worker进程之后,通过ServerLoop建立循环。其中,BackendStartup负责创建服务进程。然后开始共享内存,启动辅助进程。
三、PostgreSQL辅助进程
上图展示了PostgreSQL的部分辅助进程。
postmaster和postgres是一个进程,只是启动方式不同。当用户与postgresql数据库建立连接时,先与postmaster进程建立连接,postmaster会fork出一个子进程来为这个连接服务。fork出的子进程为服务进程。
Syslogger是指系统日志进程,当用户打开日志,它就会收集日志信息。只有在参数logging_collect设置为“on”时,主进程才会启动syslogger辅助进程。
syslogger辅助进程通过从postmaster进程、所有的服务进程及其他辅助进程,收集所有的stderr输出,并将这些输出写入到日志文件中。
bgwriter是指辅助进程,把共享内存中的脏页,写到磁盘上的进程。bgwriter辅助进程可周期性的把内存中的脏数据,刷新到磁盘中。
walwriter是指预写式日志写进程,wal是writer ahead log的缩写,中文称之为预写式日志。wal log也被简称为xlog。walwriter进程就是写wal日志的进程,预写式日志的概念就是在修改数据之前,必须要把这些修改操作记录在磁盘中。
在数据库重启后,通过读取wal日志,把最后一部分的wal日志重新执行一遍,就可以恢复到宕机时的状态。
pgarch是指归档进程,由于wal日志会被循环使用,较早时间的wal日志会被覆盖,pgarch进程会在覆盖前把wal日志备份出来。
autovacuum是指自动清理进程,在PG数据库中,对表进行DELETE操作后,旧的数据并不会立即被删除,此时旧数据只是被标识未删除状态。只有当并发的其他事务,不会读到这些旧数据时,它们才会被删除,这个清除工作就是由autovacuum进程来完成的。
pgstat是指统计数据收集进程,pgstat辅助进程主要做数据的统计收集工作,收集的信息主要用于查询优化的代价估算。系统表pg_statistic中存储了pgstat收集的各类统计信息。
接下来,演示PG后台的部分进程参数及扩展。
由于PostgreSQL将旧版本存放在relation文件中,所以dead tuple很多。随着relation文件的不断增大,导致空间不断膨胀。
为了解决这个问题,PG引入了vacuum后台进程,专门来清理dead tuple,并回缩空间。用户一般不需要手动操作vacuum,autovacuum会自动对表进行vacuum操作。
- autovacuum进程会自动执行VACUUM和ANALYZE命令。当autovacuum启用后,会自动清理,更新或删除。
- 接下来,介绍一些常用的vacuum命令。
- autovacuum_max_workers用于配置autovacuum worker的个数。
- autovacuum_freeze_max_age可以指定表上事务的最大年龄,一般默认为2亿,达到这个阀值将触发autovacuum进程,从而避免wraparound。表上的事务年龄可以通过pg_class.relfrozenxid查询。
- autovacuum_analyze_threshold和autovacuum_analyze_scale_factor参数可以控制analyze的触发的频率。
- vacuum_cost_delay:计算每个毫秒级别所允许消耗的最大IO,vacuum_cost_limit/vacuum_cost_dely。默认vacuum_cost_delay为20毫秒。
- vacuum_cost_limit:当超过此值时,vacuum会sleep。默认值为200。