清理机制为每一个表维护着一个可见性映射,它被用来跟踪哪些页面只包含对所有活动事务可见的元组。
这样做有两个目的。
第一,清理本身可以在下一次运行时跳过这样的页面,因为其中没有什么需要被清除。
第二,这允许PostgreSQL回答一些只用索引的查询,而不需要引用底层表。
PostgreSQL的 MVCC 事务语义依赖于能够比较事务 ID(XID)数字
如果一个行版本的插入 XID 大于当前事务的 XID,它就是“属于未来的”并且不应该对当前事务可见。
必要至少每 20 亿个事务就清理每个数据库中的每个表。
周期性的清理能够解决该问题的原因是,VACUUM会把行标记为 冻结,这表示它们是被一个在足够远的过去提交的事务所插入,
这样从 MVCC 的角度来看,效果就是该插入事务对所有当前和未来事务来说当然都 是可见的。
PostgreSQL保留了一个特殊的 XID(FrozenTransactionId),这个 XID 并不遵循普通 XID 的比较规则 并且总是被认为比任
何普通 XID 要老。普通 XID 使用模-232算 法来比较。
VACUUM通常会跳过不含有任何死亡行版本的页面,但是不会跳过那些含有带旧XID值的行版本的页面。
要保证所有旧的行版本都已经被冻结,需要对整个表做一次扫描。vacuum_freeze_table_age控制VACUUM什么时候这样做:如果该表经
过vacuum_freeze_table_age减去vacuum_freeze_min_age个事务还没有被完全扫描过,则会强制一次全表清扫。将这个参数设置为 0
将强制VACUUM总是扫描所有页面而实际上忽略可见性映射。
一个数据库的pg_database行的datfrozenxid列是出现在该数据库中的未冻结 XID 的下界 — 它只是数据库中每一个表的relfrozenxid值的最小值。
SELECT c.oid::regclass as table_name,
greatest(age(c.relfrozenxid),age(t.relfrozenxid)) as age
FROM pg_class c
LEFT JOIN pg_class t ON c.reltoastrelid = t.oid
WHERE c.relkind IN (\'r\', \'m\');
SELECT datname, age(datfrozenxid) FROM pg_database;
age列度量从该截断 XID 到当前事务 XID 的事务数。
VACUUM通常只扫描从上次清理后备修改过的页面,但是只有当全表被扫描时relfrozenxid才能被推进。
当relfrozenxid比vacuum_freeze_table_age个事务还老时、当VACUUM的FREEZE选项被使用时或当所有页面正好要求清理来移除死亡行版本时,全表将被扫描。
如果出于某种原因自动清理无法从一个表中清除旧的 XID,当数据库的最旧 XID 和回卷点之间达到 1 千万个事务时,系统将开始发出这样的警告消息:
WARNING: database "mydb" must be vacuumed within 177009986 transactions
HINT: To avoid a database shutdown, execute a database-wide VACUUM in "mydb".
如果这些警告被忽略,一旦距离回滚点只剩下 1 百万个事务时,该系统将会关闭并且拒绝开始任何新的事务:
ERROR: database is not accepting commands to avoid wraparound data loss in database mydb
HINT: Stop the postmaster and vacuum that database in single-user mode.
由于一旦系统进入到安全关闭模式,它将不会执行命令。做这个操作的唯一方法是停止服务器并且以单一用户启动服务器来执行VACUUM。
1.5.1. 多事务和回卷
Multixact ID被用来支持被多个事务锁定的行。
只要有多于一个事务并发地锁住一个行, 锁信息将使用一个“多个事务ID”(或简称多事务 ID)来编码。
作为一种安全设备,对任何多事务年龄超过 autovacuum_multixact_freeze_max_age的表, 都将发生一次全表清理扫描。
如果已用的成员存储空间超过总量的 50%,全表清理扫描 也将逐步在所有表上进行,这会从那些具有最老多事务年龄的表开始。
PostgreSQL有一个可选的但是被高度推荐的特性autovacuum,它的目的是自动执行VACUUM和ANALYZE 命令。
当它被启用时,自动清理会检查被大量插入、更新或删除元组的表。这些检查会利用统计信息收集功能,
因此除非track_counts被设置为true,自动清理不能被使用。在默认配置下,自动清理是被启用的并且相关配置参数已被正确配置。
#autovacuum = on
# Enable autovacuum subprocess? \'on\'
# requires track_counts to also be on.
#track_counts = on
“自动清理后台进程”实际上由多个进程组成。有一个称为 自动清理启动器的常驻后台进程, 它负责为所有数据库启动自动清理工作者进程。
在某些情况下值得周期性地使用REINDEX命令或一系列独立重构步骤来重建索引。
已经完全变成空的B树索引页面被收回重用。但是,还是有一种低效的空间利用的可能性:如果一个页面上除少量索引键之外的全部键被删除,该页面仍然被分配。
因此,在这种每个范围中大部分但不是全部键最终被删除的使用模式中,可以看到空间的使用是很差的。对于
这样的使用模式,推荐使用定期重索引。
对于非B树索引可能的膨胀还没有很好地定量分析。在使用非B树索引时定期监控索引的物理尺寸是个好主意。
对于B树索引,一个新建立的索引比更新了多次的索引访问起来要略快, 因为在新建立的索引上,逻辑上相邻的页面通常物理上也相邻
。仅仅为了提高访问速度也值得定期重索引。
REINDEX在所有情况下都可以安全和容易地使用。但是由于该命令要求一个排他表锁,因此更好的方法是用一个由创建和替换步骤组成的序列来执行索引重建。
支持带CONCURRENTLY选项的CREATE INDEX的索引类型可以用这种方式重建。