本文共 2237 字,大约阅读时间需要 7 分钟。
在 PostgreSQL 中,VACUUM(真空)是一项重要的数据库管理操作。它的主要职责是回收数据库中占用空间的已删除数据,释放磁盘空间。由于 PostgreSQL 不会实际删除数据,而是通过标记删除数据来实现,长期积累的删除数据会导致磁盘空间浪费,因此 VACUUM 是必不可少的操作。
PostgreSQL 在执行 DELETE、UPDATE 等操作时,并不会立即释放占用的磁盘空间。相反,它会将这些数据标记为删除状态,等待后续操作时进行处理。这种机制虽然提高了操作的 Atomicity(原子性),但会导致磁盘空间的浪费,特别是在频繁进行数据增删改时。
VACUUM 的核心作用就是清理这些被标记为删除但尚未回收的数据。它能够识别这些“死元组”(dead tuples),并将它们的占用空间释放,使得数据库能够更好地支持后续的操作。
PostgreSQL 的 Multi Version Concurrency Control(MVCC)机制依赖于事务 ID(transaction ID,简称 xid)的管理。每个数据元组的 header 中都包含 xmin 和 xmax,这两个值表示事务的开始和结束时间。由于 transaction ID 是循环使用的(最大为 2^32),随着时间推移,最新的事务 ID 可能会小于之前的事务 ID,这会导致 MVCC 失效。
为了解决这个问题,PostgreSQL 在背景任务中定期执行 VACUUM 操作,将过期的事务 ID 冻结(freeze)。冻结后,事务 ID 被设置为 2,这一值被视为比任何有效的事务 ID 更小。这样一来,MVCC 的原子性和一致性得以保持。
VACUUM 还会更新 PostgreSQL 的统计信息。这些统计信息包括索引的空闲空间、表的行数等。通过定期更新统计信息,优化器能够更准确地制定执行计划,从而提升查询性能。
除了清理空间和管理事务 ID,VACUUM 还会更新可见性图(visibility map)。这个图用于标记数据库页面中是否存在可见的元组。通过检查可见性图,VACUUM 扫描时可以跳过已知不可见的页面,减少不必要的 I/O 操作。同时,可见性图的大小相对较小,适合缓存到内存中,进一步提升性能。
单纯的 VACUUM 操作只会释放删除状态的空间,但不会将这些空间合并到数据库的元数据中。因此,系统仍然无法利用这些空间来存储新的数据。Full Vacuum 操作则更为彻底,它会对表和索引进行重建,并对元数据进行空间合并。
建立临时表
PostgreSQL 会创建一个新的临时表,继承原表的属性。临时表会以 RowExclusiveLock 锁的方式打开,以确保插入操作的原子性。复制数据
原数据会被复制到临时表中。同时,会清理所有已删除的元组(dead tuples)。在这个阶段,临时表和原表以及索引都会以 AccessExclusiveLock 锁的方式打开。表交换
新的临时表会替换原表,成为新的数据存储空间。再次申请 RowExclusiveLock 锁以确保数据一致性。重建索引
在表交换完成后,会对索引进行重建。重建索引时,表会以 ShareLock 锁的方式打开。删除临时表
重建完成后,临时表会被删除。临时表包含原表的物理文件,因此删除后原表的数据会被替换。在执行 Full Vacuum 时,数据库表和索引会被锁定,期间无法进行任何写操作。这意味着在业务高峰期或数据库负载较大的情况下,建议避免手动执行 Full Vacuum。合理设置自动化 vacuum 参数,定期执行常规 vacuum 操作已经足够。
PostgreSQL 提供了多种 autovacuum 参数,用于定期自动执行 vacuum 操作。主要参数包括:
对于负载较大的系统,建议合理配置 autovacuum 参数。例如,在系统资源充足的情况下,可以适当提高 autovacuum_max_workers 的值。反之,在资源有限的环境中,可以减少工作进程的数量。
在高并发、频繁更新的表中,执行 vacuum full 并不是必须的。通常情况下,定期执行常规 vacuum 操作已经足够,因为这些操作能够回收大部分已删除数据所占用的空间。
VACUUM 是 PostgreSQL 数据库管理中的关键操作,直接关系到数据库的性能和稳定性。通过合理配置 autovacuum 参数,定期执行 vacuum 操作,可以有效管理数据库空间,避免因磁盘空间不足或逻辑腐蚀带来的性能下降。
转载地址:http://bnxfk.baihongyu.com/