进程架构
本节讨论 Redrock Postgres 数据库中的进程。
在数据库术语里,PostgreSQL 使用一种客户端/服务器的模型。一次 PostgreSQL 会话由下列相关的进程(程序)组成:
- 一个服务器进程,它管理数据库文件、接受来自客户端应用与数据库的联接并且代表客户端在数据库上执行操作。该数据库服务器程序叫做
postgres
。 - 那些需要执行数据库操作的用户的客户端(前端)应用。客户端应用可能本身就是多种多样的:可以是一个面向文本的工具,也可以是一个图形界面的应用,或者是一个通过访问数据库来显示网页的网页服务器,或者是一个特制的数据库管理工具。一些客户端应用是和 PostgreSQL 发布一起提供的,但绝大部分是用户开发的。
和典型的客户端/服务器应用(C/S 应用)一样,这些客户端和服务器可以在不同的主机上。这时它们通过 TCP/IP 网络联接通讯。你应该记住的是,在客户机上可以访问的文件未必能够在数据库服务器机器上访问(或者只能用不同的文件名进行访问)。
PostgreSQL 服务器可以处理来自客户端的多个并发请求。因此,它为每个连接启动(“forks”)一个新的进程。从这个时候开始,客户端和新服务器进程就不再经过最初的 postgres
进程的干涉进行通讯。因此,主服务器进程总是在运行并等待着客户端联接,而客户端和相关联的服务器进程则是起起停停(当然,这些对用户是透明的。我们介绍这些主要是为了内容的完整性)。
Redrock Postgres 支持多线程模型,它使 Postgres 进程能够作为操作系统线程在单独的地址空间中执行。Postgres 数据库初始安装后,数据库以进程模式运行。需要将threaded_execution
参数设置为on
才能在线程模式下运行数据库。在线程模式下,UNIX 和 Linux 上的一些后台进程作为包含一个线程的进程运行,而其余的 Postgres 进程作为进程内的线程运行。因此,“Postgres 进程” 并不总是等同于 “操作系统进程”。
表 1 显示了后台进程列表。这些后台进程各自负责了系统中特定的一部分功能。
表 1. 后台进程
进程 | 描述 |
---|---|
脏数据刷写进程 | 在此进程中,共享缓冲区中的脏页被定期写入持久性存储(如机械硬盘、固态硬盘)。 |
检查点进程 | 在此进程中,执行检查点过程。 |
启动恢复进程 | 数据库服务启动时,检查控制文件和 WAL 日志文件,发现数据库发生崩溃时进行数据恢复。 |
自动分析启动器 | 定期启动自动分析工作者进程进行分析。(更准确地说,它请求 postgres 服务器创建自动分析工作者进程。) |
逻辑复制启动器 | 定期启动逻辑复制工作者进程进行数据复制。 |
回滚段维护启动器 | 定期启动回滚段维护工作者进程进行维护。 |
WAL 日志刷写进程 | 此进程定期将WAL缓冲区上的 WAL 数据写入并刷新到持久存储中。 |
统计信息收集器 | 在这个进程中,收集统计信息,用于pg_stat_activity 和pg_stat_database 等视图。 |
运行日志服务进程 | 此进程将错误消息写入日志文件。 |
WAL 日志归档进程 | 在此进程中,执行归档 WAL 日志文件。 |
有一个独立的服务器进程,叫做脏数据刷写进程,它的功能就是定期将共享缓冲区中的脏页(新的或修改过的)写入持久性存储。它写出共享缓冲区中的脏页,这样让处理用户查询的服务器进程很少或者永不等待写动作的发生。不过,脏数据刷写进程确实会增加 I/O 的总负荷,因为虽然在每个检查点间隔中一个重复弄脏的页面本来只需要写出一次,但在同一个间隔中脏数据刷写进程可能会把它写出好几次。有些配置参数可以被用于调节脏数据刷写进程的行为。
检查点进程,用于处理所有检查点。检查点在自上次检查点以来经过一定时间后自动调度,并且也可以通过 SQL 命令CHECKPOINT
向检查点进程发出信号,以执行请求的检查点。有些配置参数可以被用于调节检查点进程的行为。
检查点是在事务序列中的点,这种点保证被更新的堆和索引数据文件的所有信息在该检查点之前已被写入。在检查点时刻,所有脏数据页被刷写到磁盘,并且一个特殊的检查点记录将被写入到日志文件(修改记录之前已经被刷写到 WAL 文件)。在崩溃时,崩溃恢复过程检查最新的检查点记录用来决定从日志中的哪一点(称为重做记录)开始 REDO 操作。在这一点之前对数据文件所做的任何修改都已经被保证位于磁盘之上。因此,完成一个检查点后位于包含重做记录的日志段之前的日志段就不再需要了,可以将其回收或删除(当WAL归档工作时,日志段在被回收或删除之前必须被归档)。
检查点对于刷写所有脏数据页到磁盘的要求可能会导致可观的 I/O 负载。出于这一原因,检查点活动是被有所限制的,这样 I/O 在检查点开始时开始并且能在下一个检查点将要开始之间完成,这使得检查点期间的性能下降被最小化。
服务器的检查点进程常常自动地执行一个检查点。检查点在每 checkpoint_timeout 秒开始,或者在快要超过 max_wal_size 时开始。 默认的设置分别是 5 分钟和 1 GB。如果从前一个检查点以来没有 WAL 被写入, 则即使过了checkpoint_timeout
新的检查点也会被跳过( 如果正在使用 WAL 归档,并且你想对文件被归档频率设置一个较低的限制,来约束潜在的数据丢失,你应该调整 archive_timeout 参数而不是检查点参数)。也可以使用 SQL 命令 CHECKPOINT
来强制一个检查点。
降低checkpoint_timeout
和/或max_wal_size
会导致检查点更频繁地发生。这使得崩溃后恢复更快,因为需要重做的工作更少。但是,我们必须在这一点和增多的刷写脏数据页开销之间做出平衡。如果 full_page_writes 被设置(默认情况),则还有一个因素需要考虑。为了确保数据页一致性,在每个检查点之后对一个数据页的第一次修改将导致整个页面内容被日志记录。在这情况下,一个较小的检查点间隔会增加输出到 WAL 日志的容量,这让使用较小间隔的效果打了折扣并且将导致更多的磁盘 I/O。
检查点的代价相对比较昂贵,首先是因为它们要求写出所有当前为脏的缓冲区,正如以上讨论的,第二个原因是它们会导致额外的 WAL 流量。因此比较明智的做法是将检查点参数设置得足够高,这样检查点就不会过于频繁地发生。你可以设置 checkpoint_warning 参数作为对于你的检查点参数的一种简单完整性检查。如果检查点的发生时间间隔比checkpoint_warning
秒还要接近,一个消息将会被发送到服务器日志来推荐你增加max_wal_size
。偶尔出现的这样的消息并不会导致警报,但是如果它出现得太频繁,那么就应该增加检查点控制参数。 如果你没有把max_wal_size
设置得足够高, 那么在进行如大型COPY
传输等批量操作的时候可能会导致出现大量类似的警告消息。
为了避免大批页面写入对 I/O 系统产生的冲击,一个检查点中对脏缓冲区的写出操作被散布到一段时间上。这个时间段由 checkpoint_completion_target 控制,它用检查点间隔的一个分数表示。I/O 率将被调整,以便能按照要求完成检查点:当checkpoint_timeout
给定的秒数已经过去,或者max_wal_size
被超过之前会发生检查点,以先达到的为准。默认值为 0.5,PostgreSQL 被期望能够在下一个检查点启动之前的大约一半时间内完成每个检查点。在一个接近于正常操作期间最大I/O的系统上,你可能希望增加checkpoint_completion_target
来降低检查点的 I/O 负载。但这种做法的缺点是被延长的检查点将会影响恢复时间,因为需要保留更多WAL段来用于可能的恢复操作。尽管checkpoint_completion_target
可以被设置为高于 1.0,但最好还是让它小于 1.0(也许最多 0.9),因为检查点还包含除了写出脏缓冲区之外的其他一些动作。1.0 的设置极有可能导致检查点不能按时被完成,这可能由于所需的 WAL 段数量意外变化导致性能损失。
Redrock Postgres 有一个可选的但是被高度推荐的特性 autoanalyze,它的目的是自动执行ANALYZE
命令。在默认配置下,自动分析是被启用的并且相关配置参数已被正确配置。
“自动分析后台进程”实际上由多个进程组成。有一个称为自动分析启动器的常驻后台进程,它负责为所有数据库启动自动分析工作者进程。启动器将把工作散布在一段时间上,它每隔 autoanalyze_naptime 秒尝试在每个数据库中启动一个工作者(因此,如果安装中有N个数据库,则每 autoanalyze_naptime
/ N 秒将启动一个新的工作者)。在同一时间只允许最多 autoanalyze_max_workers 个工作者进程运行。如果有超过autoanalyze_max_workers
个数据库需要被处理,下一个数据库将在第一个工作者结束后马上被处理。每一个工作者进程将检查其数据库中的每一个表并且在需要时执行ANALYZE
。可以设置 log_autoanalyze_min_duration 来监控自动分析工作者的活动。
对于一个数据库中的工作者数量并没有限制,但是工作者确实会试图避免重复已经被其他工作者完成的工作。注意运行着的工作者的数量不会被计入 max_connections 或 superuser_reserved_connections 限制。
如果从上次ANALYZE
以来被插入、更新或删除的元组数超过了“分析阈值”,表会被分析。分析阈值定义为:
分析阈值 = 分析基本阈值 + 分析缩放系数 * 元组数
其中分析基本阈值为 autoanalyze_base_threshold,分析缩放系数为 autoanalyze_scale_factor,元组数为pg_class
.reltuples
。
临时表不能被自动分析访问。因此,临时表的分析操作必须通过会话期间的 SQL 命令来执行。
默认的阈值和缩放系数都取自于postgresql.conf
,但是可以为每一个表重写它们(和许多其他自动分析控制参数),详情参见存储参数。 如果一个设置已经通过一个表的存储参数修改,那么在处理该表时使用该值,否则使用全局设置。全局设置请参阅自动分析配置。
“逻辑复制后台进程”是由多个进程组成的。有一个称为逻辑复制启动器的常驻后台进程,它负责为所有数据库启动逻辑复制工作者进程。在数据库实例启动恢复完成后,启动器会立即查询实例中有订阅对象的数据库,启动工作者进程对这些数据库中的订阅进行数据复制。在实例运行过程中,启动器也会检查实例中新建的订阅对象,启动工作者进程对这些新建的订阅进行数据复制。在同一时间只允许最多 max_logical_replication_workers 个工作者进程运行。如果有超过max_logical_replication_workers
个订阅需要被处理,剩余的订阅将会因为没有更多的工作者而得不到处理。
逻辑复制订阅端还需要设置max_replication_slots
,它必须至少被设置为将被加入到该订阅者的订阅数。max_logical_replication_workers
必须至少被设置为订阅数加上保留给表同步的连接数。此外,可能需要调整max_worker_processes
以容纳复制工作者,至少为(max_logical_replication_workers
+ 1
)。注意,一些扩展和并行查询也会从max_worker_processes
中取得工作者槽。
“回滚段维护后台进程”是由多个进程组成的。有一个称为回滚段维护启动器的常驻后台进程,它负责为所有数据库启动回滚段维护工作者进程。在数据库实例启动恢复完成后,启动器会立即启动工作者进程对实例中的活跃回滚段进行维护。在实例运行过程中,启动器会把工作分散在一段时间上,它每隔 60 分钟尝试对数据库实例中的活跃回滚段启动一个工作者进行维护。在同一时间只允许最多 5 个工作者进程运行。如果有超过 5 个回滚段需要被处理,下一个回滚段将在第一个工作者结束后马上被处理。每一个工作者进程将检查其回滚段中的事务表,并且在发现残留事务时执行ROLLBACK
。
同时,回滚段维护后台进程,也负责对数据库回收站的清理工作。启动器会启动工作者进程清理回收站中已删除的对象(如表、索引、约束、类型等)。
有一个单独的服务器进程,称为 WAL 日志刷写进程,它试图避免会话进程不得不刷写出缓存的 WAL 数据。此外,它还保证在提交时没有立即同步到磁盘的事务提交记录(即,“异步提交”)将在限定的时间内到达磁盘,这个时间最多是wal_writer_delay
周期时间的三倍。
请注意,与共享缓冲区的脏数据刷写进程一样,当 WAL 日志刷写进程无法跟上时,会话进程仍然可以刷写出缓存的 WAL 数据。这意味着 WAL 日志刷写进程不是一个关键的后台进程,它可以在实例停止时快速关闭。