PostgreSQL 上的主动式自动清理

Cad*_*nge 46 postgresql performance vacuum performance-testing

我试图让 PostgreSQL 积极地自动清空我的数据库。我目前已按如下方式配置自动真空吸尘器:

  • autovacuum_vacuum_cost_delay = 0 #关闭基于成本的真空
  • autovacuum_vacuum_cost_limit = 10000 #最大值
  • autovacuum_vacuum_threshold = 50 #默认值
  • autovacuum_vacuum_scale_factor = 0.2 #默认值

我注意到自动真空仅在数据库未加载时才会启动,因此我遇到死元组比活元组多得多的情况。有关示例,请参阅随附的屏幕截图。其中一张表有 23 个活动元组,但有 16845 个死元组等待真空。这太疯狂了!

很多死元组

当测试运行完成并且数据库服务器空闲时,自动真空开始,这不是我想要的,因为我希望自动真空在死元组数量超过 20% 活元组 + 50 时启动,因为数据库已经配置。服务器空闲时的自动真空对我来说是无用的,因为生产服务器预计会在持续时间内达到 1000 次更新/秒,这就是为什么即使服务器负载不足我也需要自动真空运行。

有什么我想念的吗?如何在服务器负载较重时强制运行自动吸尘器?

更新

这可能是一个锁定问题吗?有问题的表是通过插入后触发器填充的汇总表。这些表以 SHARE ROW EXCLUSIVE 模式锁定,以防止并发写入同一行。

Jos*_*kus 46

Eelke 几乎肯定是正确的,您的锁定正在阻止 autovacuum。Autovacuum 旨在让位于用户活动,故意。如果这些表被锁定,则 autovacuum 无法清空它们。

然而,对于后人,我想为超强自动真空提供一组示例设置,因为您提供的设置并没有完全做到这一点。请注意,使 autovacuum 更具侵略性不太可能解决您的问题。另请注意,默认 autovacuum 设置基于使用 DBT2 运行 200 多次测试运行以寻求最佳设置组合,因此应假定默认值是好的,除非您有充分的理由不这么想,或者除非您的数据库明显超出OLTP 数据库的主流(例如每秒获得 10K 更新的微型数据库,或 3TB 数据仓库)。

首先,打开日志记录,以便您可以检查 autovacuum 是否正在执行您认为的操作:

log_autovacuum_min_duration = 0
Run Code Online (Sandbox Code Playgroud)

然后让我们让更多的 autovac 工人并让他们更频繁地检查表:

autovacuum_max_workers = 6
autovacuum_naptime = 15s
Run Code Online (Sandbox Code Playgroud)

让我们降低自动真空和自动分析的阈值以更快触发:

autovacuum_vacuum_threshold = 25
autovacuum_vacuum_scale_factor = 0.1

autovacuum_analyze_threshold = 10
autovacuum_analyze_scale_factor = 0.05 
Run Code Online (Sandbox Code Playgroud)

然后让我们减少 autovacuum 的可中断性,使其完成得更快,但代价是对并发用户活动产生更大的影响:

autovacuum_vacuum_cost_delay = 10ms
autovacuum_vacuum_cost_limit = 1000
Run Code Online (Sandbox Code Playgroud)

有用于一般激进自动清理的完整程序,这可能适用于获得非常高更新率的小型数据库,但可能对并发用户活动产生太大影响。

另请注意,可以根据table调整 autovacuum 参数,这几乎总是需要调整 autovacuum 行为的更好答案。

不过,它不太可能解决您的真正问题。


小智 38

只是为了查看哪些表完全符合 autovacuum 的条件,可以使用以下查询(基于http://www.postgresql.org/docs/current/static/routine-vacuuming.html)。但是请注意,该查询不会查找特定于表的设置:

 SELECT psut.relname,
     to_char(psut.last_vacuum, 'YYYY-MM-DD HH24:MI') as last_vacuum,
     to_char(psut.last_autovacuum, 'YYYY-MM-DD HH24:MI') as last_autovacuum,
     to_char(pg_class.reltuples, '9G999G999G999') AS n_tup,
     to_char(psut.n_dead_tup, '9G999G999G999') AS dead_tup,
     to_char(CAST(current_setting('autovacuum_vacuum_threshold') AS bigint)
         + (CAST(current_setting('autovacuum_vacuum_scale_factor') AS numeric)
            * pg_class.reltuples), '9G999G999G999') AS av_threshold,
     CASE
         WHEN CAST(current_setting('autovacuum_vacuum_threshold') AS bigint)
             + (CAST(current_setting('autovacuum_vacuum_scale_factor') AS numeric)
                * pg_class.reltuples) < psut.n_dead_tup
         THEN '*'
         ELSE ''
     END AS expect_av
 FROM pg_stat_user_tables psut
     JOIN pg_class on psut.relid = pg_class.oid
 ORDER BY 1;
Run Code Online (Sandbox Code Playgroud)


小智 11

是的,这是一个锁定问题。根据此页面(非完整),VACUUM 需要 SHARE UPDATE EXCLUSIVE 访问,该访问被您正在使用的锁定级别阻止。

你确定你需要这个锁吗?PostgreSQL 符合 ACID,因此并发写入在大多数情况下不是问题,因为如果发生序列化违规,PostgreSQL 将中止其中一个事务。

您也可以通过使用SELECT FOR UPDATE锁定行而不是整个表来锁定行。

另一种没有锁定的替代方法是使用事务隔离级别可序列化。但是,这可能会影响其他事务的性能,您应该为更多的序列化失败做好准备。


小智 8

现有的“qualify for autovacuum”脚本非常有用,但(如正确所述)缺少表特定选项。这是它的修改版本,它考虑了这些选项:

WITH rel_set AS
(
    SELECT
        oid,
        CASE split_part(split_part(array_to_string(reloptions, ','), 'autovacuum_vacuum_threshold=', 2), ',', 1)
            WHEN '' THEN NULL
        ELSE split_part(split_part(array_to_string(reloptions, ','), 'autovacuum_vacuum_threshold=', 2), ',', 1)::BIGINT
        END AS rel_av_vac_threshold,
        CASE split_part(split_part(array_to_string(reloptions, ','), 'autovacuum_vacuum_scale_factor=', 2), ',', 1)
            WHEN '' THEN NULL
        ELSE split_part(split_part(array_to_string(reloptions, ','), 'autovacuum_vacuum_scale_factor=', 2), ',', 1)::NUMERIC
        END AS rel_av_vac_scale_factor
    FROM pg_class
) 
SELECT
    PSUT.relname,
    to_char(PSUT.last_vacuum, 'YYYY-MM-DD HH24:MI')     AS last_vacuum,
    to_char(PSUT.last_autovacuum, 'YYYY-MM-DD HH24:MI') AS last_autovacuum,
    to_char(C.reltuples, '9G999G999G999')               AS n_tup,
    to_char(PSUT.n_dead_tup, '9G999G999G999')           AS dead_tup,
    to_char(coalesce(RS.rel_av_vac_threshold, current_setting('autovacuum_vacuum_threshold')::BIGINT) + coalesce(RS.rel_av_vac_scale_factor, current_setting('autovacuum_vacuum_scale_factor')::NUMERIC) * C.reltuples, '9G999G999G999') AS av_threshold,
    CASE
        WHEN (coalesce(RS.rel_av_vac_threshold, current_setting('autovacuum_vacuum_threshold')::BIGINT) + coalesce(RS.rel_av_vac_scale_factor, current_setting('autovacuum_vacuum_scale_factor')::NUMERIC) * C.reltuples) < PSUT.n_dead_tup
        THEN '*'
    ELSE ''
    END AS expect_av
FROM
    pg_stat_user_tables PSUT
    JOIN pg_class C
        ON PSUT.relid = C.oid
    JOIN rel_set RS
        ON PSUT.relid = RS.oid
ORDER BY C.reltuples DESC;
Run Code Online (Sandbox Code Playgroud)


Cra*_*ein 6

增加 autovacuum 进程的数量和减少小睡时间可能会有所帮助。这是我在存储备份信息的服务器上使用的 PostgreSQL 9.1 的配置,因此获得了大量插入活动。

http://www.postgresql.org/docs/current/static/runtime-config-autovacuum.html

autovacuum_max_workers = 6              # max number of autovacuum subprocesses
autovacuum_naptime = 10         # time between autovacuum runs
autovacuum_vacuum_cost_delay = 20ms     # default vacuum cost delay for
Run Code Online (Sandbox Code Playgroud)

我也会尝试降低cost_delay使吸尘更具侵略性。

我还可以使用 pgbench 测试自动清理。

http://wiki.postgresql.org/wiki/Pgbenchtesting

高竞争示例:

创建 bench_replication 数据库

pgbench -i -p 5433 bench_replication
Run Code Online (Sandbox Code Playgroud)

运行 pgbench

pgbench -U postgres -p 5432 -c 64 -j 4 -T 600 bench_replication
Run Code Online (Sandbox Code Playgroud)

检查自动清扫状态

psql
>\connect bench_replicaiton
bench_replication=# select schemaname, relname, last_autovacuum from pg_stat_user_tables;
 schemaname |     relname      |        last_autovacuum        
------------+------------------+-------------------------------
 public     | pgbench_branches | 2012-07-18 18:15:34.494932+02
 public     | pgbench_history  | 
 public     | pgbench_tellers  | 2012-07-18 18:14:06.526437+02
 public     | pgbench_accounts | 
Run Code Online (Sandbox Code Playgroud)