mirror of
https://github.com/Vonng/ddia.git
synced 2024-12-06 15:20:12 +08:00
141 lines
11 KiB
Markdown
141 lines
11 KiB
Markdown
# Autovacuum Tuning Basics [译]
|
||
|
||
原文:https://blog.2ndquadrant.com/autovacuum-tuning-basics/#PostgreSQL%20Performance%20Tuning
|
||
|
||
几个星期前,我介绍了调优检查点的基础知识,在那篇文章中,我还提到了性能问题的第二个常见原因是autovacuum(根据我们在邮件列表和我们的客户支持下看到的)。所以让我跟随这个帖子关于自动调谐的基础知识。我将非常简要地解释必要的理论(死元组,膨胀和autovacuum如何处理它),但是这篇博文的主要重点是调优 - 那里有什么配置选项,经验法则等等。
|
||
|
||
### 死元组
|
||
|
||
首先,让我们简单地解释一下“死亡元组”和“膨胀”(如果你想得到更详细的解释,可以阅读Joe Nelson的文章,详细讨论这个)。
|
||
|
||
当您在PostgreSQL中执行DELETE时,行(又名元组)不会立即从数据文件中删除。相反,它只能通过在行首部中设置xmax字段来标记为已删除。对于UPDATE,也可以看作PostgreSQL中的DELETE + INSERT。
|
||
|
||
这是PostgreSQL MVCC背后的基本思想之一,因为它允许更大的并发性,只需在不同的进程之间进行最小程度的锁定。这个MVCC实现的缺点当然是它留下了被删除的元组,甚至在所有可能看到这些版本的事务完成之后。
|
||
|
||
如果不清理,这些“死元组”(对于任何事务实际上是不可见的)将永远停留在数据文件中,浪费磁盘空间,而对于有许多DELETE和UPDATE的表,死元组可能会占用绝大部分磁盘空间。当然,这些死元组也会被索引引用,进一步增加了浪费的磁盘空间。这就是我们在PostgreSQL中所谓的“膨胀”。当然,查询必须处理的数据越多(即使99%的数据立即被“抛弃”),查询就越慢。
|
||
|
||
### Vacuum和AutoVacuum
|
||
|
||
回收死元组占用的空间的最直接的方法是(通过手动运行VACUUM命令)。这个维护命令将扫描表并从表和索引中删除死元组 - 它通常不会将磁盘空间返回给操作系统,但会使其可用于新行。
|
||
|
||
注意:`VACUUM FULL`会回收空间并将其返回给操作系统,但是有一些缺点。首先它获得对表的**独占锁(Access Exclusive)**,阻止所有操作(包括SELECT)。其次,它本质上创建了表的副本,使所需的磁盘空间增加了一倍,所以当磁盘空间已经不足时不太实际。
|
||
|
||
`VACUUM`的麻烦在于它完全是手动操作 - 只有在您决定运行时才会发生,而不是在需要时才会发生。你可以把它放到cron中,每5分钟在所有的表上运行一次,但是大部分运行的机会并不会真正清理任何东西,唯一的影响是CPU和I / O在系统上的使用率会更高。或者你可能每天晚上只运行一次,在这种情况下,你可能会累积更多你想要的死元组。
|
||
|
||
这是我们使用**自动清理(AutoVacuum)**的主要目的;根据需要进行清理,以控制浪费空间的数量。数据库确实知道随着时间的推移生成了多少个死元组(每个事务都报告它删除和更新的元组的数量),当表累积了一定数量的死元组时,可以触发清理工作(默认情况下是20%表,我们将会看到)。所以在繁忙的时候会更频繁地执行,而当数据库大部分空闲的时候则更少。
|
||
|
||
### autoanalyze
|
||
|
||
清理死元组不是自动清理的唯一任务。它还负责更新优化程序在规划查询时使用的数据分布统计信息。您可以通过运行ANALYZE来手动收集这些数据,但是会遇到与VACUUM类似的问题 - 您可能会经常运行或不经常运行。
|
||
|
||
解决方案也是类似的 - 数据库可以观察表中有多少行更改,并自动运行ANALYZE。
|
||
|
||
注意:ANALYZE的开销要大一点,因为虽然VACUUM的成本与死元组的数量成正比(开销很小,当死元组数量很少时/没有),ANALYZE必须在每次执行时从头开始重建统计数据。另一方面,如果你不是经常运行它,那么不好的计划选择的代价可能同样严重。
|
||
|
||
为了简洁起见,我会在后面的文章中大部分忽略这个自动清理任务 - 配置与清理非常相似,并且遵循大致相同的推理。
|
||
|
||
### 监控
|
||
|
||
在进行任何调整之前,您需要能够收集相关数据,否则您怎么能说您需要进行任何调整,或评估配置更改的影响?
|
||
|
||
换句话说,你应该有一些基本的监控,从数据库中收集指标。为了清理,你至少要看这些值:
|
||
|
||
|
||
* `pg_stat_all_tables.n_dead_tup` :每个表中的死元组数(用户表和系统目录)
|
||
* `(n_dead_tup / n_live_tup)` : 每个表中死/活元组的比率
|
||
* `(pg_class.relpages / pg_class.reltuples)`: 每行空格
|
||
|
||
|
||
如果您已经部署了监控系统(应该的),那么您很有可能已经收集了这些指标。总体目标是获得稳定的行为,这些指标没有突然或显着的变化。
|
||
|
||
还有一个方便的pgstattuple扩展,允许您对表和索引执行分析,包括计算可用空间量,死元组等。
|
||
|
||
### 调整目标
|
||
|
||
在查看实际配置参数之前,让我们简要讨论一下高级调优目标,即更改参数时要实现的目标:
|
||
|
||
清理死元组 - 保持合理低的磁盘空间量,而不是浪费不合理的磁盘空间量,防止索引膨胀并保持查询速度。
|
||
最大限度地减少清理的影响 - 不要经常执行清理工作,因为这会浪费资源(CPU,I / O和RAM),并可能会严重影响性能。
|
||
|
||
也就是说,你需要找到适当的平衡 - 经常运行可能会不够经常运行。天平在很大程度上取决于您所管理的数据量,您正在处理的工作负载类型(DELETE / UPDATE数量)。
|
||
|
||
`postgresql.conf`中的大多数默认值是相当保守的,原因有二。首先,根据当时常见的资源(CPU,RAM,...),默认值是几年前决定的。其次,我们希望默认配置可以在任何地方工作,包括树莓派(Raspberry Pi)或小型VPS服务器等小型机器。对于许多部署(特别是较小的部署和/或处理大部分读取工作负载),默认的配置参数将工作得很好。
|
||
|
||
随着数据库大小和/或写入数量的增加,问题开始出现。典型的问题是清理不经常发生,当发生这种情况时会大大地干扰性能,因为它必须处理大量的垃圾。如果这些情况你应该遵循这个简单的规则:
|
||
|
||
* 如果它造成了损失,你不会经常这样做。
|
||
|
||
|
||
* 也就是说,调整参数,使得清理更频繁,每次处理更少量的死元组。
|
||
|
||
注意:人们有时遵循不同的规则 - 如果伤害了,不要这样做。 - 完全禁用自动清理。除非你真的(真的)知道你在做什么,并且有定期的清理脚本,否则请不要这样做。否则,你正在画一个角落,而不是性能有些下降,你将不得不面对严重退化的表现,甚至可能是停机。
|
||
|
||
所以,现在我们知道我们想要通过调谐实现什么,让我们看看配置参数...
|
||
|
||
### 阈值和比例因子
|
||
|
||
当然,你可能会调整的第一件事情是当清理被触发,这是受两个参数的影响:
|
||
|
||
autovacuum_vacuum_threshold = 50
|
||
autovacuum_vacuum_scale_factor = 0.2
|
||
并且每当无用元组的数目(你可以看到pg_stat_all_tables.n_dead_tup)超过
|
||
|
||
阈值+ pg_class.reltuples * scale_factor
|
||
|
||
该表将被视为需要清理。该公式基本上说,高达20%的表可能是清理前的死元组(50行的阈值是为了防止对小表进行非常频繁的清理)。
|
||
|
||
默认比例因子适用于中小型的表格,但对于非常大型的表格来说效果并不理想 - 在10GB的表格上,这大概是2GB的死元组,而在1TB的表格上则是200GB。
|
||
|
||
这是一个积累了大量死元组的例子,并且一次处理所有的元组,这将会受到伤害。根据之前提到的规则,解决方案是通过显着降低比例因子来更频繁地执行此操作,甚至可能是这样:
|
||
|
||
autovacuum_vacuum_scale_factor = 0.01
|
||
|
||
这将限制仅减少到表格的1%。另一种解决方法是完全放弃比例因子,仅使用阈值
|
||
|
||
autovacuum_vacuum_scale_factor = 0
|
||
autovacuum_vacuum_threshold = 10000
|
||
|
||
这应该在产生10000个死元组之后触发清理。
|
||
|
||
一个麻烦的问题是postgresql.conf中的这些变化会影响到所有表(实际上是整个集群),并且可能会对小型表(包括系统目录)的清理产生不利影响。
|
||
|
||
当小桌子被更频繁地清理时,最简单的解决办法就是完全忽略这个问题。小桌子的清理将是相当便宜的,并改善工人数量
|
||
|
||
还没有提到的一个配置选项是autovacuum_max_workers,那是什么意思?那么,清理不会发生在一个自动清理过程中,但是数据库可以启动到实际清理不同数据库/表的autovacuum_max_workers进程。
|
||
|
||
这很有用,因为例如你不想停止清理小表,直到完成一个大表的清理(这可能需要相当多的时间,因为节流)。
|
||
|
||
麻烦的是用户假定工作人员的数量与可能发生的清理量成正比。如果将自动清扫工人的数量增加到6人,那么与默认的3人相比,工作量肯定会增加一倍,对吧?
|
||
|
||
那么,不。几段前面描述的成本上限是全球性的,所有的汽车真空工人都是共同的。每个工作进程只获得总成本限制的1 / autovacuum_max_workers,所以增加工作者数量只会使他们变慢。
|
||
|
||
这有点像高速公路 - 车辆数量增加一倍,但速度减半,只能让你每小时到达目的地的人数相同。
|
||
|
||
所以如果你的数据库的清理跟不上用户的活动,那么增加工作者的数量并不是一个解决方案,除非你也调整了其他的参数。
|
||
|
||
### 按表限流
|
||
|
||
实际上,当我说成本限制是全局的,所有的自动清理Worker共同分担的时候,我一直在说谎。与缩放因子和阈值类似,可以设置每个表的成本限制和延迟:
|
||
|
||
```sql
|
||
ALTER TABLE t SET (autovacuum_vacuum_cost_limit = 1000);
|
||
ALTER TABLE t SET (autovacuum_vacuum_cost_delay = 10);
|
||
```
|
||
|
||
全球成本计算中不包括处理这些表的工人,而是独立地进行限制。
|
||
|
||
这给了你相当多的灵活性和权力,但不要忘记 - 拥有巨大的权力是很大的责任!
|
||
|
||
在实践中,我们几乎从不使用这个功能,有两个基本的原因。首先,您通常希望在后台清理上使用单个全局限制。其次,多个工人有时被扼杀在一起,有时独立地使监督和分析系统的行为变得更加困难。
|
||
|
||
### 概要
|
||
|
||
所以这就是你调整自动清理的方法。如果我必须把它归纳成几条基本规则,那就是这五条:
|
||
|
||
* 说真的,不要禁用autovacuum,除非你真的知道你在做什么。
|
||
* 在繁忙的数据库上(做大量更新和删除),特别是大数据库,你可能应该减小比例因子,这样更频繁地进行清理。
|
||
* 在合理的硬件(良好的存储,多核心)上,你应该增加节流参数,以便清理能够跟上。
|
||
* 单独增加autovacuum_max_workers在大多数情况下并不会真的有帮助。你会得到更多的进程变慢。
|
||
* 你可以使用ALTER TABLE来设置每个表的参数,但是如果你真的需要的话,可以考虑一下。这使得系统更复杂,更难以检查。
|
||
|
||
我原本包括几个部分,解释什么时候autovacuum没有真正的工作,以及如何检测它们(以及什么是最好的解决方案),但博客文章已经太长了,所以我会在几天后分开发布。 |