PostgreSQL/架构
PostgreSQL 实现了一种 客户端-服务器架构。每个 客户端进程
连接到服务器端的单个 后端进程
。
客户端无法直接访问数据库文件及其存储的数据。相反,它们向 服务器
发送请求,并从那里接收请求的数据。服务器为每个客户端连接启动一个进程。这样的后端进程通过操作 共享内存
处理客户端的请求。这会导致 实例
的其他活动(文件访问、WAL、真空等)。实例是一组在公共共享内存上运行的服务器端进程。PostgreSQL 不使用线程。
在启动时,实例由一个名为 Postmaster
的单个进程启动。它加载配置文件,分配共享内存,并启动实例的其他协作进程:后台写入器
、检查点
、WAL 写入器
、WAL 归档器
、自动真空
、统计收集器
、日志记录器
等等。随后,Postmaster 监听其配置的系统端口。响应新的客户端连接尝试,它启动新的后端进程,并将身份验证、通信和所有后续请求的处理委托给它们。下图可视化了 RAM、进程、文件及其协作的主要方面。
像 SELECT 或 UPDATE 这样的客户端请求通常会导致读取或写入数据的必要性。这是由客户端的后端进程执行的。这种 I/O 活动 **不会直接在磁盘上完成**。相反,它们在共享内存中的缓存中完成,该缓存镜像文件页面。访问此类缓存比访问磁盘快得多。读取访问仅影响缓存,而写入访问是通过写入日志来完成的,即所谓的预写日志或 WAL。
共享内存的大小有限,可能需要清除页面。只要此类页面的内容没有改变,这就不成问题。但它们可能会被修改。修改后的页面称为脏页面(或脏缓冲区),在清除它们之前,必须将它们写回磁盘。后台写入器进程和检查点负责此操作。它们确保缓存在短时间延迟后与文件同步。从 RAM 到磁盘的同步包括两个步骤。
首先,每当页面的内容发生变化时,都会创建一个 WAL 记录
,其中包含增量信息(旧内容和新内容之间的差异)并存储在共享内存的另一个区域中。在 COMMIT
或更早之前,WAL 写入器进程读取它们并将它们追加到当前 WAL 文件
的末尾。这种顺序写入比写入堆文件和索引文件的随机位置快。在将脏页面本身传输到磁盘的第二步之前,必须将来自一个脏页面的所有 WAL 记录传输到磁盘。
其次,后台写入器进程将脏缓冲区从共享内存传输到文件。由于 I/O 活动可能会阻塞其他进程,因此它会定期启动,并且只运行一小段时间。这样做,其广泛的(也是昂贵的)I/O 活动分散在一段时间内,避免了令人衰弱的 I/O 峰值。检查点进程也将脏缓冲区传输到文件。
检查点进程通过写入和刷新所有较旧的脏缓冲区、所有较旧的 WAL 记录以及最终将特殊的检查点记录写入磁盘来创建 检查点
。因此,检查点是事务序列中的一点,在该点保证堆文件和索引文件已使用该检查点之前的 tất cả các trang bẩn được cập nhật。
WAL 文件包含对数据的更改。这种“增量信息”在系统崩溃的情况下用于恢复(数据库备份 + WAL 文件 --> 崩溃之前的数据库)。因此,WAL 文件应被复制并保存在安全的地方,直到下一次数据库备份完成。这是 WAL 归档器进程的职责。可以配置它运行一个脚本,只要 WAL 文件已满并切换到下一个文件,该脚本就会复制 WAL 文件。当然,出于安全原因,此类副本应完成到单独的磁盘或服务器。但将原始 WAL 文件存储在与堆文件和索引文件不同的磁盘上也是一个好主意。这种分离提高了性能。这可以通过使用指向不同磁盘上的目录的符号链接来实现。
自动真空进程将堆和索引文件中的不再被任何事务使用的旧记录版本标记为“最终删除”。因此,它释放了它们占用的空间以供重用。需要这种进程是由于 MVCC 架构造成的。
统计收集器收集有关对 SQL 对象(如表、行、索引、页面等)的访问量的计数器,并将它们存储在系统表中。
日志记录器将有关在数据库访问期间可能发生的或多或少严重事件(例如,错误密码、无权限、长时间运行的查询等)的文本行写入顺序文件。