PostgreSQL autovacuum原理详解

1、概述

PostgreSQL的Vacuum可以帮助清除表中的死元组并释放空间,但是表上什么时候需要行vacuum操作呢?
一般来说不需要我们去手动进行vacuum操作,autovacuum进程会自动去对需要的表进行vacuum操作。
autovacuum进程会自动执行VACUUM和ANALYZE命令。启用后,将自动清理将检查具有大量插入,更新或删除的元组的表,然后根据阈值对表进行清理或分析。
那么autovacuum的阈值是多少呢?我们怎么才能知道哪些对象需要进行vacuum呢?

2、autovacuum详解

vacuum的阈值:
vacuum_threshold =
vacuum_base_threshold + vacuum_scale_factor * number of tuples

analyze的阈值:
analyze_threshold =
analyze_base_threshold + analyze_scale_factor * number of tuples

上面的计算公式中:

  • vacuum_base_threshold默认是50;
  • vacuum_scale_factor默认是0.2;
  • number of tuples即表的行数。

例如一张1000行数据的表,触发vacuum的阈值便是:50 + 0.2 * 1000 = 250,即表中有250行的数据发生更新时,autovacuum进程便会对其进行vacuum操作。

例如:
创建一张新表:

bill@bill=>create table t_vacuum(id int,info text);
CREATE TABLE
bill@bill=>insert into t_vacuum select generate_series(1,1000),md5(random()::text);
INSERT 0 1000
bill@bill=>SELECT schemaname,relname,last_autovacuum,last_autoanalyze FROM pg_stat_all_tables where relname='t_vacuum';
 schemaname | relname  | last_autovacuum | last_autoanalyze
------------+----------+-----------------+------------------
 public     | t_vacuum |                 |
(1 row)

可以看到刚刚创建的这张表不在被即将被vacuum的对象中。

接着,更新该表:

bill@bill=>update t_vacuum set info = 'bill' where id < 800;
UPDATE 799

然后再查看:

bill@bill=>SELECT schemaname,relname,last_autovacuum,last_autoanalyze FROM pg_stat_all_tables where relname='t_vacuum';
 schemaname | relname  |        last_autovacuum        |       last_autoanalyze
------------+----------+-------------------------------+-------------------------------
 public     | t_vacuum | 2021-01-05 16:43:15.384256+08 | 2021-01-05 16:43:15.388081+08
(1 row)

日志中信息如下:

2021-01-05 16:43:11.985 CST,"bill","bill",19036,"[local]",5ff418d3.4a5c,7,"COMMIT",2021-01-05 15:44:19 CST,4/2010662,0,WARNING,25P01,"there is no transaction in progress",,,,,,,,"EndTransactionBlock, xact.c:3838","psql","client backend"
2021-01-05 16:43:15.384 CST,,,11152,,5ff426a3.2b90,1,,2021-01-05 16:43:15 CST,5/46503,0,LOG,00000,"automatic vacuum of table ""bill.public.t_vacuum"": index scans: 0
pages: 0 removed, 13 remain, 0 skipped due to pins, 0 skipped frozen
tuples: 799 removed, 1000 remain, 0 are dead but not yet removable, oldest xmin: 1597
buffer usage: 54 hits, 2 misses, 3 dirtied
avg read rate: 9.533 MB/s, avg write rate: 14.300 MB/s

但是这样有个问题,如果我的表数据量很大,例如100万条记录,那么我只能等到更新了20万条记录时才能被vacuum?

显然这不太合理,所以我们可以自己去修改相关的参数来解决这类问题。

我们可以通过下列SQL去检查表中的dead tuples:

SELECT *,
       n_dead_tup > av_threshold AS "av_needed",
       CASE
         WHEN reltuples > 0 THEN
          round(100.0 * n_dead_tup / (reltuples))
         ELSE
          0
       END AS pct_dead
  FROM (SELECT N.nspname,
               C.relname,
               pg_stat_get_tuples_inserted(C.oid) AS n_tup_ins,
               pg_stat_get_tuples_updated(C.oid) AS n_tup_upd,
               pg_stat_get_tuples_deleted(C.oid) AS n_tup_del,
               pg_stat_get_live_tuples(C.oid) AS n_live_tup,
               pg_stat_get_dead_tuples(C.oid) AS n_dead_tup,
               C.reltuples AS reltuples,
               round(current_setting('autovacuum_vacuum_threshold')
                     ::integer +
                     current_setting('autovacuum_vacuum_scale_factor')
                     ::numeric * C.reltuples) AS av_threshold,
               date_trunc('minute',
                          greatest(pg_stat_get_last_vacuum_time(C.oid),
                                   pg_stat_get_last_autovacuum_time(C.oid))) AS last_vacuum,
               date_trunc('minute',
                          greatest(pg_stat_get_last_analyze_time(C.oid),
                                   pg_stat_get_last_analyze_time(C.oid))) AS last_analyze
          FROM pg_class C
          LEFT JOIN pg_namespace N
            ON (N.oid = C.relnamespace)
         WHERE C.relkind IN ('r', 't')
           AND N.nspname NOT IN ('pg_catalog', 'information_schema')
           AND N.nspname ! '^pg_toast') AS av
 ORDER BY av_needed DESC, n_dead_tup DESC;

3、autovacuum参数调优

多数情况下,Autovacuum是PostgreSQL决定何时清理表的时间,但是对我们来说查看表并确定表是否需要自动清理同样重要,即使它不是自动清理的候选者也是如此。

以下是autovacuum相关的一些参数:

  • autovacuum_vacuum_threshold = 50
  • autovacuum_vacuum_scale_factor = 0.2
  • vacuum_cost_page_hit = 1
  • vacuum_cost_page_miss = 10
  • vacuum_cost_page_dirty = 20
  • autovacuum_vacuum_cost_delay = 20ms
  • autovacuum_vacuum_cost_limit = 200

例如上面的例子,如果我们设置autovacuum_vacuum_scale_factor为0.02,那么对于100万数据的表更新2万条记录便会进行vacuum。

但是这样会带来另一个问题:其它的小表参数也会受到影响。因此我们可以只针对单张表去设置该参数:

ALTER TABLE t SET (autovacuum_vacuum_scale_factor = 0.02);
ALTER TABLE t SET (autovacuum_vacuum_threshold = 100);

4、autovacuum工作原理

autovacuum会从磁盘中连续读取表的8KB页面,并修改/写入包含无效元组的页面。这是一项占用大量资源(CPU和磁盘I / O)的操作,如果页面读取不包含任何无效元组,它将忽略该页面。

进行vacuum的cost基于下面三个参数:

  • vacuum_cost_page_hit:这意味着页面已经在共享缓冲区中了,默认为1。
  • vacuum_cost_page_miss: 这意味着必须从磁盘读取页面,默认为10。
  • vacuum_cost_page_dirty:这意味着由于元组失效,必须修改从磁盘读取的页面,默认为20。

一旦成本总和达到autovacuum_cost_limit(autovaccum默认为200,手动VACUUM禁用),VACUUM进程将休眠,而autovacuum_vacuum_cost_delay不执行任何操作(默认20 ms)。

使用默认参数,这意味着autovacuum最多将4MB / s写入磁盘,并从磁盘或OS页面缓存中读取8MB / s。

总结:

1.除非非常重要,否则不要禁用autovacuum。
2.在高OLTP机器上,建议在表上有一个手动的VACUUM。
2.在事务计数较高的数据库上降低autovacuum_vacuum_scale_factor。
3.在具有旧的非活动数据的表上调整autovacuum_vacuum_scale_factor。
5.根据硬件调整相关参数以提高性能。

猜你喜欢

转载自blog.csdn.net/weixin_39540651/article/details/112250546