PostgresSQL-平常清理

22.1. 平常清理

因为如下几个缘由,必须周期性运行 PostgreSQL 的 VACUUM 命令∶php

  1. 恢复那些由已更新的或已删除的行占据的磁盘空间。html

  2. 更新 PostgreSQL 查询规划器使用的数据统计信息。算法

  3. 避免由于事务 ID 重叠形成的老旧数据的丢失。sql

对上面每一个条件进行 VACUUM 操做的频率和范围因不一样的节点而不一样。 所以,数据库管理员必须理解这些问题而且开发出合适的维护策略。 本节的重点就放在解释这些高级别的问题; 至于命令语法的细节,请参阅 VACUUM 命令手册页。数据库

从 PostgreSQL 7.2 开始, VACUUM 的标准形式能够和普通的数据库操做 (selects, inserts, updates, deletes, 但不包括表定义的修改)。 所以平常的清理也再也不象之前的版本那样具备干扰性, 也再也不那么特别要求安排在天天的低使用的时间里进行。后端

从 PostgreSQL 8.0 开始,有一些配置参数能够设置, 用来进一步减少后端清理的的性能影响。参阅 Section 17.4.4安全

在 PostgreSQL 8.1 中增长了一个自动的机制,用于执行必要的 VACUUM 操做。参阅 Section 22.1.4服务器

22.1.1. 恢复磁盘空间

在正常的 PostgreSQL 操做里, 对一行的UPDATEDELETE并未当即删除旧版本的数据行。 这个方法对于获取多版本并行控制的好处是必要的(参阅 Chapter 12): 若是一个行的版本仍有可能被其它事务看到,那么你就不能删除它。 但到了最后,不会有任何事务对过时的或者已经删除的元组感兴趣。 而它占据的空间必须为那些新的元组使用而回收, 以免对磁盘空间增加的无休止的需求。这件事是经过运行 VACUUM 实现的。并发

很明显,那些常常更新或者删除元组的表须要比那些较少更新的表清理的更频繁一些。 因此,设置一个周期性的 cron 任务 VACUUM 那些选定的表,而忽略那些已经知道变化比较少的表. 这个方法只是在你拥有大量更新频繁的表和大量不多更新的表的时候有意义 — 清理一个小表的额外开销根本不值得担忧.mvc

VACUUM 命令有两个变种。第一种形式,叫作"懒汉 vacuum"或者只是 VACUUM, 在表和索引中标记过时的数据为未来使用;它并试图当即恢复这些过时数据使用的空间。 所以,表文件不会缩小,而且任何文件中没有使用的空间都不会返回给操做系统。 这个变种的 VACUUM 能够和一般的数据库操做并发执行。

第二种形式是 VACUUM FULL 命令。 这个形式使用一种更加激进的算法来恢复过时的的行版本占据的空间。 任何 VACUUM FULL 释放的空间都当即返回给操做系统。 可是,这个形式的VACUUM 命令在进行 VACUUM FULL 一个表的时候在其上要求一个排他锁。 所以,常用 VACUUM FULL 会对并发数据库查询有着很是糟糕的影响。

标准形式的 VACUUM 最适合用于维护至关程度的磁盘用量的稳定状态。 若是你须要把磁盘空间归还给操做系统,那么你可使用 VACUUM FULL — 不过若是释放的磁盘空间又会很快再次被分配又怎样? 若是维护更新频繁的表,那么中等频率的屡次标准 VACUUM 运行方法比很低频率的 VACUUM FULL 更好。

对于大多数节点而言,咱们推荐的习惯是在一天中的低使用的时段安排一次整个数据库的 VACUUM, 必要时外加对更新频繁的表的更常常的清理。 (有些环境下,对那些更新很是频繁的表可能会每几分钟就 VACUUM 一次。) 若是你的集群中有多个数据库,别忘记对每一个库进行清理; vacuumdb 脚本可能会帮你的忙。

若是你知道本身刚删除掉一个表中大部分的行,那么咱们建议使用VACUUM FULL, 这样该表的稳定态尺寸能够由于VACUUM FULL更富侵略性的方法而显著减少。 平常的磁盘空间清理,请使用 VACUUM,而不是 VACUUM FULL

若是你有一个表,它的内容常常被彻底删除,那么能够考虑用 TRUNCATE,而不是后面跟着 VACUUM 的 DELETE。 TRUNCATE 当即删除整个表的内容, 而不要求随后的 VACUUM 或者VACUUM FULL 来恢复如今没有用的磁盘空间。

22.1.2. 更新规划器统计

PostgreSQL 的查询规划器依赖一些有关表内容的统计信息用觉得查询生成好的规划。 这些统计是经过ANALYZE 命令得到的,你能够直接调用这条命令, 也能够把它当作 VACUUM 里的一个可选步骤来调用。 拥有合理准确的统计是很是重要的,不然,选择了恶劣的规划极可能下降数据库的性能。

和为了回收空间作清理同样,常常更新统计信息也是对更新频繁的表更有用。 不过,即便是更新很是频繁的表,若是它的数据的统计分布并不常常改变,那么也不须要更新统计信息。 一条简单的拇指定律就是想一想表中字段的最大很最小值改变的幅度。 好比,一个包含行更新时间的 timestamp 字段将是随着行的追加和更新稳定增加最大值的; 这样的字段可能须要比那些包含访问网站的 URL 的字段更频繁一些更新统计信息。 那些 URL 字段可能改变得同样频繁,可是其数值的统计分布的改变相对要缓慢得多。

咱们能够在特定的表,甚至是表中特定的字段上运行 ANALYZE, 因此若是你的应用有需求的话,咱们是能够对某些信息更新得比其它信息更频繁的。 不过,在实际中,这种作法的有用性是值得怀疑的。 从 PostgreSQL 7.2 开始, ANALYZE 是一项至关快的操做,即时在大表上也很快, 由于它使用了统计学上的随机采样的方法进行行采样, 而不是把每一行都读取进来。所以,每隔一段时间对整个数据库运行一便这条命令可能更简单。

提示: 尽管用 ANALYZE 按字段进行挖掘的方式可能不是很实用, 但你可能仍是会发现值得按字段对 ANALYZE 收集的统计信息的详细级别进行调整。 那些常常在WHERE子句里使用的字段若是有很是不规则的数据分布, 那么就可能须要比其它字段更细致的数据图表.参阅 ALTER TABLE SET STATISTICS.

咱们对大多数节点都建议在天天的低使用时段安排一次数据库范围的 ANALYZE: 这个任务能够有效地和天天的 VACUUM 组合在一块儿。 不过,这对那些表统计信息改变相对缓慢的节点可能会过于夸张, 并且少一些的 ANALYZE 也足够了。

22.1.3. 避免事务 ID 重叠形成的问题

PostgreSQL 的 MVCC 事务语意依赖于比较事务 ID(XID)的数值: 一条带有大于当前事务的 XID 的插入 XID 的行版本是"属于将来的", 而且不该为当前事务可见。可是由于事务 ID 的大小有限(在咱们写这些的时候是 32 位), 若是一次集群若是运行的时间很长(大于 4 十亿次事务), 那么它就要受到事务 ID 重叠的折磨:XID 计数器回到零位, 而后忽然间全部之前的事务就变成看上去是在未来的 — 这意味着它们的输出将变得可见。 简而言之,可怕的数据丢失,(实际上数据仍然在那里,可是若是你没法获取数据,这么说也只是幸灾乐祸。)

在 PostgreSQL 7.2 以前, 防护 XID 重叠的惟一办法就是至少每4十亿事务就从新作一次initdb。 这种作法对高流量的节点而言固然不是使人满意的作法,因此咱们设计了更好的方法。 新的方法容许某个服务器仍然保持运行状态,不须要 initdb 或者任何类型的重启。 代价就是下面这样的维护要求: 数据库中的每一个表都必须在每十亿次事务中至少清理一次 

从实际角度出发,这个要求不算一个很繁重的要求, 可是由于若是咱们没能知足这个要求的后果是所有数据的丢失(而不只仅是磁盘空间的浪费或者性能的降低), 咱们制做了一些特殊的东西来帮助数据库管理员避免灾难的发生。 对于集群中的每一个数据库,PostgreSQL 都跟踪自上次全数据库范围 VACUUM 以来的时间。 若是任何数据库接近了十亿次事务的危险级别,系统就开始发出警告信息。 若是什么都不干,那么系统最终会中止正常的操做,直到进行了合适的手工操做。 本节剩下的部分给出这方面的细节。

XID 比较的新方法剥离出两个特殊的 XID,数字 1 和 2 (BootstrapXID 和 FrozenXID)。 这两个 XID 老是被认为表任何普通的 XID 旧。普通的 XID(那些大于 2 的)使用模-231运算进行比较。 这就意味着对于每一个普通的 XID,老是有二十亿个 XID 是"更旧"以及二十亿个 XID"更新"; 表达这个意思的另一个方法是普通的 XID 空间是没有终点的环。 所以,一旦一条元组带着特定的普通 XID 建立出来,那么该元组 将在之后的二十亿次事务中表现得是"在过去",而无论咱们说的是哪一个普通 XID。 若是该元组在超过二十亿次事务以后仍然存在, 那么它就会忽然变成在未来的元组。为了不数据丢失, 老的元组必须在到达二十亿次事务的年龄以前的某个时候赋予 XID FrozenXID。 一旦它被赋予了这个特殊的 XID,那么它们在全部普通事务面前表现为 "在过去",而无论事务 ID 是否重叠, 所以这样的元组直到删除以前都会无缺,无论要保存多长时间.这个 XID 的从新赋值是VACUUM 控制的.

VACUUM 的正常策略是给任何其普通 XID 有超过十亿次已过去事务行版本从新赋值为 FrozenXID。 这个策略保留了原来的插入 XID 直到该数值再也不使人感兴趣为止。 (实际上,大多数行版本将可能在尚未"冻结"以前就完成生存和消亡了)。 在这个策略下,任何表在两次 VACUUM 运行之间的最大的安全间隔是十亿次事务: 若是你等的时间更长,那么最后就可能就会有一条不够老的行版本在从新赋值时变成比二十亿次事务更老, 并所以重叠到了将来 — 也就是说,你失去它了。(固然,它在另外二十亿次事务以后会从新出现,不过那样也无济于事。)

由于上面的缘由,咱们须要周期性地运行 VACUUM, 因此很难有哪一个表会到十亿次事务尚未清理过。可是,为了帮助管理员确保知足了这个要求, VACUUM 在系统表pg_database 里存储了事务 ID 统计。 尤为是一个数据库的 pg_database 行中的 datfrozenxid 字段在任何数据库范围的 VACUUM 操做(也就是没有声明任何表的VACUUM)以后将会被更新。 这个字段里存储的数值是该 VACUUM 命令使用的冻结终止的 XID。 系统保证在该数据库中全部比这个终止 XID 老的普通 XID 都被 FrozenXID 代替。 检查这个信息的一个便利的方法是执行下面的查询

SELECT datname, age(datfrozenxid) FROM pg_database;

age 字段用于测量从停止 XID 到当前事务的 XID 的数目。

使用了这种标准的冻结策略,对一个刚清理过的数据库而言, age 字段将从十亿处开始。当age到达二十亿次的时候, 数据库必须再次清理以免事务标识重叠形成的问题。 咱们建议的策略是至少每半个十亿次(5亿次)事务 VACUUM 一次数据库, 这样就能够保证足够的安全边界范围.为了帮助知足这条规则, 若是有任何 pg_database 记录显示出超过15亿次事务的 age, 那么每次数据库范围的VACUUM 都会自动发出一条警告,好比:

play=# VACUUM;
WARNING:  database "mydb" must be vacuumed within 177009986 transactions
HINT:  To avoid a database shutdown, execute a full-database VACUUM in "mydb".
VACUUM

若是忽略了上面这样的 VACUUM 信息,若是距离事务 ID 重叠小于 1 千万次, 那么 PostgreSQL 就会在每次事务开始前发出相似上面的警告。 若是这些警告仍是被忽略了,那么系统将在距离重叠小于 1 百万次的时候关闭,而且拒绝执行任何新的事务:

play=# select 2+2;
ERROR:  database is shut down to avoid wraparound data loss in database "mydb"
HINT:  Stop the postmaster and use a standalone backend to VACUUM in "mydb".

这个 1 百万的事务安全边界留下来用于让管理员在不丢失数据的状况下进行恢复, 方法是手工执行所须要的 VACUUM 命令。不过,由于一旦进入了安全关闭模式,系统就不能再执行命令, 作这件事情的惟一的方法是中止 postmaster,使用一个单独运行的后端来执行 VACUUM。 关闭模式不会强制于独立运行的后端。参阅 postgres 手册也获取有关使用独立运行后端的细节。

带着 FREEZE 选项的 VACUUM 使用了更大胆的冻结策略: 若是行版本已经老得被全部打开的事务看作是良好的, 那么就都冻结.特别是若是在一个空闲的数据库上运行 VACUUM FREEZE,那么就保证该数据库中全部的行版本都被冻结。 所以,只要该数据库没有其它的变化,那么它就不须要后续的清理以免事务 ID 重叠问题。 这个技巧被 initdb 用于准备template0数据库。 咱们也应该用这个方法对全部在 pg_database表里标记着 datallowconn = false的数据库进行初始化, 由于咱们尚未任何便利的方法 VACUUM 一个你没法联接的数据库。

22.1.4. auto-vacuum 守护进程

从 PostgreSQL 8.1 开始,系统带有一个额外的可选服务进程, 叫作 autovacuum 守护进程,它的目的是自动执行 VACUUM 和 ANALYZE 命令。在打开这个选项以后,autovacuum 守护进程将周期性运行而且检查那些有大量插入,更新或者删除元组操做的表。 这些检查使用行级别的统计收集设施;所以,除非把 stats_row_level 和 stats_row_level 设置为 true,不然没法使用 autovacuum 守护。 还有,在为 superuser_reserved_connections 选择数值的时候,不要忘记给 autovacuum 进程保留一个槽位。

若是打开了 autovacuum 守护,那么它会每隔 autovacuum_naptime 秒钟运行一次,而且检查应该处理哪一个数据库。 任何临近事务 ID 重叠的数据库都会被当即处理。这个时候,autovacuum 发出一个数据库反胃的 VACUUM 调用,若是是模板数据库,则发出 VACUUM FREEZE, 而后终止。若是没有数据库复合这个标准,则选择被上次 autovacuum 处理时间最远的那个数据库。 这种状况下,该数据库里的表被检查,而后根据须要发出独立的 VACUUM 或者 ANALYZE 命令。

对于每一个表,用两个条件来判断应该使用哪一个操做。 若是上次 VACUUM 以后的过时元组的数量超过了"清理阈值(vacuum threshold)", 那么就清理改表。清理阈值是定义为:

清理阈值 = 清理基本阈值 + 清理缩放系数 * 元组数
(vacuum threshold = vacuum base threshold + vacuum scale factor * number of tuples)

这里的清理基本阈值是 autovacuum_vacuum_threshold, 清理的缩放系数是 autovacuum_vacuum_scale_factor, 元组的数目是 失效的元组数目是从统计收集器里面获取的;这事一个半精确的计数,由每次 UPDATE 和 DELETE 操做更新。 (它只是半精确的是由于在重载下,有些信息可能会丢失。) 为了分析,使用了一个相似的条件:分析阈值,定义为

分析阈值 = 分析基本阈值 + 分析缩放系数 * 元组数目
(analyze threshold = analyze base threshold + analyze scale factor * number of tuples)

它会和上次 ANALYZE 插入,更新,或者删除的元组总数进行比较。

缺省的阈值和伸缩系数是从 postgresql.conf 里面取得的, 不过,咱们能够以每一个表独立设置的方式覆盖它,方法就是在系统表 pg_autovacuum 里输入记录。 若是 pg_autovacuum 里面存在对某个特定表的行,那么就使用它声明的设置; 不然使用全局设置。参阅 Section 17.9 获取有关全局设置的更多细节。

除了基本阈值和缩放系数以外,在 pg_autovacuum 里还有三个参数能够为每一个表进行设置。 首先,pg_autovacuum.enabled 能够设置为 false, 让 autovacuum 守护进程彻底忽略某个表。这种状况下,autovacuum 只有在为了不事务 ID 重叠清理整个数据库的时候才会动那个表。另外两个参数,清理开销延迟 (pg_autovacuum.vac_cost_delay)和清理开销限制 (pg_autovacuum.vac_cost_limit), 用于为 基于开销的清理延迟 特性设置表相关的数值。

若是在 pg_autovacuum 里任何数值设置为负数, 或者在 pg_autovacuum 里就根本没有出现特定表的数据行, 那么使用 postgresql.conf 里面对应的数值。

目前没有任何制做 pg_autovacuum 记录的支持, 只能手工向该系统表中 INSERT。这个特性将在之后的版本中改进, 而且这个系统表的定义也颇有可能会改变。

相关文章
相关标签/搜索