autovacuum 最典型的任务是清理由UPDATE或DELETE操作产生的死元组。如果autovacuum无法清理死元组,您可以按照以下三个步骤进行调优:
确保没有任何事情阻止 autovacuum 回收死元组
有时,autovacuum 不会删除死元组。大多数情况下,问题原因在那些长时间运行的事务。除非您能消除这些障碍,否则调整 autovacuum 将毫无用处。
如果您无法从根本上解决问题,则可以使用配置参数 idle_in_transaction_session_timeout,让 PostgreSQL 终止那些停留在 “idle in transaction” 时间过长的会话。这会导致客户端出现错误,但如果没有其他方法来保持数据库正常运行,那可能也是合理的。同样,要消除长时间运行的查询,您可以使用参数 statement_timeout。
优化 autovacuum 以加速运行
如果 autovacuum 无法跟上清理死元组的步伐,解决方案是让它工作得更快。这样看来是显而易见的,但许多人会陷入这样的陷阱,即认为,让 autovacuum 更早启动或更频繁地运行可以解决问题。
VACUUM是一种资源密集型操作,因此默认情况下 autovacuum 故意运行缓慢。目的是让它在后台工作,而不妨碍正常的数据库操作。但是,如果你的工作负载创建了大量的死元组,你将不得不使它更积极地工作:
- • 调大 autovacuum_vacuum_cost_limit 的值,该参数默认值为 200(这是温和的方式)。
- • 降低 autovacuum_vacuum_cost_delay 的值,该参数从默认值 2 毫秒(在旧版本中为 20 毫秒)(这是有效的方式)。
将autovacuum_vacuum_cost_delay设置为 0,将使 autovacuum 与手动VACUUM一样快,也就是尽可能的快。
由于不是所有表都以相同的速度增加死元组,因此通常最好不要更改postgresql.conf中的全局设置,而是单独更改那些繁忙的表的设置:
ALTER TABLE busy_table SET (autovacuum_vacuum_cost_delay = 1);
对表进行分区也有助于更快地完成清理工作。这样做的好处是,您可以让多个 autovacuum 工作进程并行处理多个分区,这样清理这些分区表的完成速度就比单个 autovacuum 工作进程要更快。
优化业务负载,以产生更少的死元组
如果其他方法都不起作用,则必须看下能否产生更少的死元组。也许一行的多次UPDATE可以合并成一次UPDATE?
通常,您可以使用 “HOT 更新” 来显著减少死元组的数量:
- • 设置表的 fillfactor 为一个小于 100 的值,以便INSERT时在每个块中留下一些空闲空间。
- • 请确保您在UPDATE中修改的列,是没有索引的列。
然后,任何SELECT或 DML 语句都可以清理死元组,这样就不太需要VACUUM了。