在PostgreSQL实例的性能优化过程中,定期检查热点路径上运行的语句是一个良好的实践。可以通过检查`pg_stat_statements`视图来了解SQL语句的执行统计信息,其中包含`shared_blks_dirtied`和`shared_blks_written`列,分别记录语句修改的共享缓冲区缓存块数以及写入磁盘的块数。
令人意外的是,`SELECT`查询也可能在这些列中出现非零值,这表明简单的读取操作也可能导致数据写入。本文将深入探讨导致这种情况的两个机制:
**1. 事务状态跟踪的提示位更新:**
PostgreSQL使用多版本并发控制(MVCC)来支持多个客户端并发读取和写入数据。每个逻辑表行都可能存在多个版本,确保客户端始终拥有数据的一致视图,即使其他客户端同时修改相同的数据。为了避免脏读,PostgreSQL会检查事务是否已提交,并在读取相关元组时更新元组头部的提示位以记录结果。这个过程可能会导致读取操作修改页面,从而产生写入操作。
**2. 页面修剪:**
页面修剪是一种轻量级的垃圾回收机制,用于删除不再可见的旧元组版本。页面修剪通常在页面可用空间不足且修剪可能有用时触发。当访问页面时,会检查页面头部的提示字段(`prune_xid`),以判断是否需要进行页面修剪。如果页面可用空间小于10%或者小于`fillfactor`百分比,则会触发页面修剪机制。页面修剪也可能导致读取操作修改页面,产生写入操作。
文章通过创建示例表及相关操作演示了这两个机制的工作原理,并详细介绍了PostgreSQL的页面结构、元组版本管理、HOT更新等方面的内容。
最后,文章总结了PostgreSQL中读取操作导致写入的正常现象,并建议开发者在遇到类似情况时无需感到意外,而是将其视为服务器正常运转的体现。文章也推荐了学习PostgreSQL内部机制的资料,帮助读者进一步深入了解数据库的运作原理。