暂无图片
暂无图片
3
暂无图片
暂无图片
暂无图片

PostgreSQL细说autovacuum(一)

原创 yBmZlQzJ 2022-12-23
1899

什么是MVCC

与其他 RDBMS 相比,PostgreSQL 中 MVCC(多版本并发控制)的实现是不同和特殊的。PostgreSQL 中的 MVCC 通过版本控制控制哪些元组对事务可见。

让我们考虑一下 Oracle 或 MySQL 数据库的情况。当您对一行执行 DELETE 或 UPDATE 时会发生什么?您会看到在全局 UNDO 段中维护的 UNDO 记录。这个 UNDO 段包含了过去的一行图像,以帮助数据库实现一致性。(ACID 中的“C”)。例如,如果有一个旧事务依赖于被删除的行,则该行可能仍然对它可见,因为过去的图像仍然保留在 UNDO 中。如果您是阅读此博文的 Oracle DBA,您可能会很快想起错误 ORA - 01555 snapshot too old  . 这个错误的意思是——你可能有一个较小的 undo_retention 或没有一个巨大的 UNDO 段,它可以保留现有或旧交易所需的所有过去的图像(版本)。

PostgreSQL是如何管理UNDO?

简单来说,PostgreSQL 在自己的 Table 中同时维护了某一行的过去镜像和最新镜像。这意味着,UNDO 是在每个表中维护的。 这是通过版本控制完成的。现在,我们可能会得到一个提示,PostgreSQL 表的每一行都有一个版本号。为了了解如何在每个表中维护这些版本,您应该了解 PostgreSQL 中表的隐藏列(xmin)。


xmin

xmin标识此行版本的插入事务的事务id(xid),更新后,将插入一个新的行版本

db_sqlfx=# select txid_current();
 txid_current 
--------------
    205692369
(1 row)

db_sqlfx=# insert into test values(999999999,'张三');
INSERT 0 1
db_sqlfx=# select xmin,xmax,cmin,cmax,* from test where n_id = 999999999;
   xmin    | xmax | cmin | cmax |   n_id    | c_name | c_bz | dt_cjsj | j_ext 
-----------+------+------+------+-----------+--------+------+---------+-------
 205692370 |    0 |    0 |    0 | 999999999 | 张三   |      |         | 
(1 row)

可以看到txid_current获取到了当前的事务id,当插入一行后递增的事务id为205692370。当更新的时候原本的数据被标记为删除,然后插入一条新的数据,这样就造成了脏数据,而vacuum的主要作用之一便是清理这些脏数据

事物ID

xid是32位的整数,在3到最大值之间循环使用,0到2是特殊值

每产生一个事务ID,pg_clog下的commit log都会占用2bit(事务状态)

没有做任何实际变更的事务,只会产生vxid(虚拟事务ID)

InvalidTransactionId = 0

BootstrapTransactionId = 1

FrozenTransactionId = 2

vacuum作用

  • 清理垃圾数据,重用无效元组的磁盘空间

  • 冻结tuple的xid

  • 更新visibility map(vm文件),可以加快 index-only scan 的速度

  • 更新统计信息,提高执行计划效率

  • 防止数据丢失,防止数据库回卷造成数据库进入单机模式,回收旧事务ID的工作也是由vacuum来完成,这个动作是与清理旧版本数据一起完成的

vacuum基础

VACUUM 方式有以下两种:
♠ VACUUM
♠ VACUUM FULL
VACUUM FULL 可以回收磁盘空间但是运行很慢。而VACUUM 可以在并行运行在生产数据库中。在执行 VACUUM 期间,DML 和DQL 语句可以正常运行,但是此时不能在表对象上执行表定义操作。VACUUM FULL 运行期间,将会获取表中的排他锁,因此不能并行执行。通常情况下,建议使用 VACUUM,而不是 VACUUM FULL

对于不带有 FULL 参数的 VACUUM ,仅仅将 dead tuple 从表对应的物理文件中标识移除,但是不释放空间。此刻,表对象数据文件依然使用同一个数据文件。
对于带有 FULL 参数的 VACUUM ,该操作实际将表对应的物理文件内容重写到新的磁盘文件中,从而释放磁盘空间。
对于没有 FULL参数的 VACUUM 操作,由于没有排他锁,表可以正常读取和写入。对于有 FULL 参数的 VACUUM 操作,会获取表排他锁,因此比不带 FULL 参数要慢。

autovacuum和vacuum

  • vacuum是一件很重要的事,如果没有及时 vacuum,轻则数据膨胀,重则数据库宕机。

  • 为了减轻DBA的压力,PostgreSQL设计的 Autovacuum,即自动进行垃圾回收。

  • 默认情况下Autovacuum是打开的。

autovacuum触发条件

  • 1、表上(update,delete 记录) >= autovacuum_vacuum_scale_factor* reltuples(表上记录数) + autovacuum_vacuum_threshold

  • 如果表有10000行,则表中的行如果更新或删除的数量超过50+10000*0.2=2050行,autovacuum就开始工作,

  • 而100万行数据需要修改50+1000000*0.2=200050行才会触发autovacuum

  • 所以随着一个表的数据量增多,触发aotovacuum会越来越少,最终导致表的膨胀越来越大

  • 2、表上事务的最大年龄配置参数autovacuum_freeze_max_age,默认为2亿,达到这个阀值将触发 autovacuum进程,从而避免 wraparound。

  • 表年龄:select relname,age(relfrozenxid) from pg_class where relname='test_1';

当然除了系统自动触发外,也可以手动触发:

vacuum:对表进行vacuum

analyze:对表进行analyze

vacuum analyze:进行vacuum和analyze

可见性映射-VM

VM 是可见性映射文件表,每一个表都有可见性映射文件,以跟踪页面中包含已知对所有活跃事务可见的元组,同时也跟踪页面仅包含冻结的元组。该文件命名以表对象 oid作为前缀,_vm 作为后缀存在。
可见性映射是一个位图,每个heap page 都有两个位,表示全部页面可见和全部页面冻结。如果可见位被设置,意味着页面上所有元组对于所有事务都是可见的,因此该页面不需要清理。如果冻结位被设置,意味着页面上所有的元组都被完全冻结,因此即使需要对整个表进行vacuum(如:anti-wraparound),也无需要对该页面进行 vacuum处理。仅当页面全部已经可见时,才需要对全冻结位设置。

vacuum步骤

  • 数据库中autovacuum由autovacuum launcher进程定期的拉起autovacuum worker线程来进行工作

  • autovacuum launcher 是守护进程,autovacuum worker 是实际进行工作的进程。

  • 第一部分

  • 1.从指定的表中依次处理每一张表,

  • 2.获取表上的ShareUpdateExclusiveLock锁(该锁允许其他事物对该表进行读取)。

  • 3.扫描表中的所有页面,获取所有的死亡元组,(死元组的列表存储在本地内存的maintenance_work_mem里)

  • 4.如果有必要,冻结旧的元组的事务标识

  • 5.移除指向死亡元组的索引元组

  • 第二部分

  • 1.移除每一页中的死亡元组,并对每一页内的的活元组进行碎片整理,重排本页的活元组

  • 2.更新已经处理的空闲空间映射(FSM)和可见性映射(VM)

  • PG会不断执行这个过程直至最后一页

  • 第三部分

  • 1.如果最后一个页面没有任何元组,则截断最后一页

  • 2.更新与冻结事务标识相关的系统视图(pg_class与pg_database)

  • 3.释放ShareUpdateExclusiveLock锁

  • 第四部分

  • 1.更新一些统计信息(pg_stat_all_tables等)

  • 2.移除不必要的提交日志文件,移除CLOG(10版本及以后为xact)中的非必要文件与页面

  • (当更新pg_database.datfrozenxid时,会尝试删除不必要的CLOG)

具体的步骤,参考灿灿的帖子https://mp.weixin.qq.com/s?__biz=MzUyOTAyMzMyNg==&mid=2247486426&idx=1&sn=fddea4ff7f87866f53337db6c9853928&chksm=fa6623ebcd11aafd6e0d07a35a1ec49bcb1dd98aef4a99c5e0f3e8751b528ec0a8d2a6735009&token=953894319&lang=zh_CN#rd

常见问题

  • 阻塞DDL

  • Autovacuum运行,影响性能

  • Autovacuum 长时间运行,数天

  • Autovacuum 不工作 --long transaction.

  • 数据库的表在正常情况下会自动回收空间(dead row versions),如果表被长事务(idle in transaction)访问的情况下,就不能自动回收表的dead row versions,造成表空间持续增长

长事务阻塞autovacuum

--方便查看,我们将log_autovacuum_min_duration设置为0记录日志
postgres=# show log_autovacuum_min_duration;
 log_autovacuum_min_duration 
-----------------------------
 2s
(1 row)

postgres=# alter system set log_autovacuum_min_duration  = 0;
ALTER SYSTEM
postgres=# select  pg_reload_conf();
 pg_reload_conf 
----------------
 t
(1 row)

postgres=# create table tbl_1(id int,name varchar(100));
CREATE TABLE
--根据触发条件,只需要修改一条数据便会触发autovacuum
postgres=# alter table tbl_1 set (autovacuum_vacuum_scale_factor=0,autovacuum_vacuum_threshold=1);
ALTER TABLE
postgres=# insert into tbl_1 values(1,'a'),(2,'b'),(3,'c'),(4,'d');
INSERT 0 4
postgres=# select relname,relpages,reltuples,pg_stat_get_live_tuples(oid),pg_stat_get_dead_tuples(oid) from pg_class where relname = 'tbl_1';
 relname | relpages | reltuples | pg_stat_get_live_tuples | pg_stat_get_dead_tuples 
---------+----------+-----------+-------------------------+-------------------------
 tbl_1   |        0 |         0 |                       4 |                       0
(1 row)
postgres=# analyze tbl_1;
ANALYZE
--更新统计信息后可以看到页和行正常
postgres=# select relname,relpages,reltuples,pg_stat_get_live_tuples(oid),pg_stat_get_dead_tuples(oid) from pg_class where relname = 'tbl_1';
 relname | relpages | reltuples | pg_stat_get_live_tuples | pg_stat_get_dead_tuples 
---------+----------+-----------+-------------------------+-------------------------
 tbl_1   |        1 |         4 |                       4 |                       0
(1 row)
  • 开启session 1,更新数据不提交

    postgres=# begin;
    BEGIN
    postgres=# update tbl_1 set name ='zs' where id = 1;
    UPDATE 1
  • session 2,删除两条数据,此处会触发autovacuum

postgres=# delete from tbl_1 where id >2;
DELETE 2

--此时查看数据有2条脏数据
postgres=# select relname,relpages,reltuples,pg_stat_get_live_tuples(oid),pg_stat_get_dead_tuples(oid) from pg_class where relname = 'tbl_1';
 relname | relpages | reltuples | pg_stat_get_live_tuples | pg_stat_get_dead_tuples 
---------+----------+-----------+-------------------------+-------------------------
 tbl_1   |        1 |         4 |                       2 |                       2
(1 row)
  • 查看数据库日志

-- autovacuum触发了,但是有些行无法移除,在查询中说明涉及到的行是会被保护的,
--提示oldest xmin: 206119124。该 xmin 是造成不能 vacuum 的事务 id,根据提示信息,
--有尚未提交的事务,导致autovacuum 进程不能 vacuum。


2022-12-14 11:01:48.130 CST,,,2361,,63993c9c.939,1,,2022-12-14 11:01:48 CST,24/1178790,0,LOG,00000,
"automatic vacuum of table ""postgres.public.tbl_1"": index scans: 0
pages: 0 removed, 1 remain, 0 skipped due to pins, 0 skipped frozen
tuples: 0 removed, 4 remain, 2 are dead but not yet removable, oldest xmin: 206119124
buffer usage: 27 hits, 0 misses, 0 dirtied
avg read rate: 0.000 MB/s, avg write rate: 0.000 MB/s
system usage: CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s",,,,,,,,,""

--查看更新的xmin,判断出确实就是这个事务id造成的不能自动清理
postgres=# select xmin,* from tbl_1 where id = 1;
   xmin    | id | name 
-----------+----+------
 206119124 |  1 | zs
(1 row)
  • 查看206119124,这是一个idle in transaction的事务

postgres=# select * from pg_stat_activity where backend_xid = 206119124;
-[ RECORD 2 ]----+-----------------------------------------------------
datid            | 13593
datname          | postgres
pid              | 1306
usesysid         | 10
usename          | sa
application_name | psql
client_addr      | 
client_hostname  | 
client_port      | -1
backend_start    | 2022-12-14 10:56:18.692901+08
xact_start       | 2022-12-14 10:56:21.330269+08
query_start      | 2022-12-14 11:00:16.976885+08
state_change     | 2022-12-14 11:00:16.978206+08
wait_event_type  | Client
wait_event       | ClientRead
state            | idle in transaction
backend_xid      | 206119124
backend_xmin     | 
query            | update tbl_1 set name ='zs' where id = 1;
backend_type     | client backend
  • 提交session 1的事务

  postgres=# begin;
  BEGIN
  postgres=# update tbl_1 set name ='zs' where id = 1;
  UPDATE 1
  postgres=# commit;
  COMMIT

--观察数据库的脏数据已经被清理
postgres=# select relname,relpages,reltuples,pg_stat_get_live_tuples(oid),pg_stat_get_dead_tuples(oid) from pg_class where relname = 'tbl_1';
 relname | relpages | reltuples | pg_stat_get_live_tuples | pg_stat_get_dead_tuples 
---------+----------+-----------+-------------------------+-------------------------
 tbl_1   |        1 |         2 |                       2 |                       0
(1 row)

--数据库日志显示脏数据也已经被清理
2022-12-14 11:03:48.169 CST,,,2769,,63993d14.ad1,1,,2022-12-14 11:03:48 CST,24/1178822,0,LOG,00000,
"automatic vacuum of table ""postgres.public.tbl_1"": index scans: 0
pages: 0 removed, 1 remain, 0 skipped due to pins, 0 skipped frozen
tuples: 4 removed, 2 remain, 0 are dead but not yet removable, oldest xmin: 206119136
buffer usage: 28 hits, 0 misses, 1 dirtied
avg read rate: 0.000 MB/s, avg write rate: 7.154 MB/s
system usage: CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s",,,,,,,,,""

1、在存在长事务的时候,触发autovacuum后死元组是不被清理的;

2、当长事务提交,但是条件不够触发autovacuum时,可以使用vacuum手动进行数据清理;

3、如果使用vacuum清理的时候如果长事务未提交,也是不能清理死元组的。

vacuum 和analyze的关系

同vacuum一样analyze也有参数控制

vacuum:

autovacuum_vacuum_scale_factor:0.2
autovacuum_vacuum_threshold:50

analyze:

autovacuum_analyze_scale_factor:0.1
autovacuum_analyze_threshold:50

举个例子,如果一张表有100万数据,那么当修改的数据超过100050(1000000x0.1+50)条数据的时候就会触发analyze,但是不会触发aotovacuum,当修改的数据量超过200050(1000000x0.2+50)这个时候就会同时触发aotovacuum和autoanalyze,所以一般触发autovacuum的时候都伴随着autoanalyze。

表年龄和数据库年龄

pg_database记录了这个数据库中最旧的表年龄,也就是pg_class中年龄最大的,没有记录是那张表

相关参数

vacuum_freeze_min_age

vacuum_freeze_table_age

autovacuum_freeze_max_age

表膨胀的原因

  • 巡检pg_stat_all_tables.n_dead_tup查看具体的 表有多少行旧数据没有清理

  • 数据库上有长时间没有提交的事务:

  • select * from pg_stat_activity,看状态是Idle  in transaction的长时间事物

  • 设置参数:idle_in_transaction_session_timeout

  • 未结束的2PC事务

  • SELECT gid, prepared, owner, database, transaction AS xmin FROM pg_prepared_xacts ORDER BY age(transaction) DESC;

  • ROLLBACK PREPARED transaction_id;

  • COMMIT PREPARED transaction_id;

  • 未使用的复制槽(replication slots)

  • SELECT slot_name, slot_type, database,  xmin FROM pg_replication_slots  ORDER BY age(xmin) DESC;

  • 逻辑复制

  • 设置old_snapshot_threshold

  • 强制回收旧版本数据

  • 表太大,vacuum花了很久还没有整理完

  • vacuum在单张表上无法并发

  • 使用分区表,在PG11及以下版本,建议使用 pg_pathman做分区

  • 加大maintenance_work_mem或 autovacuum_work_mem的值

  • vacuum_defer_cleanup_age的值设置太大

  • 备库上有长时间未结束的长查询

  • 同时设置了hot_standby_feedback

  • 这种情况出现的概率很低

  • 密集的DML操作强制取消了autovacuum

  • 关闭了autovacuum

  • 上面这些操作中的一个或者多种情况的组合都会导致表膨胀,事务ID环绕

如何防患未然

  • 超大表分表或让autovacuum不整理一些大表,可以在大表上关闭autovacuum参数,手工整理 这些大表

  • alter table test01  set(autovacuum_enabled=off);

  • 或者对大表单独进行参数设置

  • ALTER TABLE test_data  SET (autovacuum_analyze_scale_factor = 0.0);

  • ALTER TABLE test_data  SET (autovacuum_analyze_threshold = 50000);

  • 这样test_data表没修改50000条数据就会进行自动的清理

设置delay参数,对大表设置频繁的vacuum,一次清理少量的数据比大量的数据跟快,io更平缓

  • xid监控,分级告警

  • 长事务监控

  • 添加大表监控告警

  • 达到vacuum触发的条件,但是未完成vacuum的监控

  • 曾加定时vacuum cron

监控

  • 监控过期数据比例

SELECT schemaname,
relname,
n_live_tup,
n_dead_tup,
round(n_dead_tup::numeric/(n_dead_tup::numeric +n_live_tup::numeric +100)*100 ,2) as percent_of_dead_tuples,
last_autovacuum 
FROM pg_stat_user_tables where n_dead_tup >10 order by n_dead_tup desc;
 schemaname |      relname       | n_live_tup | n_dead_tup | percent_of_dead_tuples |        last_autovacuum        
------------+--------------------+------------+------------+------------------------+-------------------------------
 public     | tbl                |        100 |         30 |                  13.04 | 
 (1 rows)
  • vacuum进度

select pid,datname,relid,phase,(heap_blks_scanned ::float + 1::float)/(heap_blks_total::float + 1::float) * 100 as scanned_percent  
from pg_stat_progress_vacuum ;

监控年龄,如果没有冻结,那么数据库多半有问题,

比如长事务,或者高并发影响了回收,后面会触发强制自动清理功能

调优

  • 增加autovacuum_work_mem可以加快清理速度

  • 可以增加到1-2GB,如果没有设置为默认值(-1)那么久根据maintenance_work_mem的值来进行设置,1 GB的维护工作内存足以一次处理大约 1.79 亿个死元组

  • autovacuum_max_works指定启动多少个autovacuum的进程进行垃圾回收

  • 默认值是3,建议在写压力大的数据库中调成6或更大的值

  • autovacuum_naptime: autovacuum做完一 轮的时间如果小于这个值,则会sleep到这 个时间。指调度的频繁程度

  • 默认值是1min,可以保持不变。

  • autovacuum_vacuum_scale_factor、autovacuum_vacuum_threshold

  • 指定表的数据量变化多少后就需要vacuum

  • autovacuum_vacuum_scale_factor默认值是0.2,即 20%

  • autovacuum_vacuum_threshold默认值是50行

  • 即当表的变更数据的行数超过:20%的行+50行时, vacuum就会开始操作这张表。

  • 对于大表来说,20%可能太大了,需要设置的小一些。

  • alter table test01 set  (autovacuum_vacuum_scale_factor=0.05);

  • autovacuum是尽量控制表的年龄在 autovacuum_freeze_max_age的范围内,但如果因各种原因(如autovacuum整理的过慢),还是 有事物ID回卷问题导致数据库宕机的风险。

  • 所以需要监控数据库中表的年龄

  • 方法是:select datname,age(datfrozenxid) from  pg_database;

  • 即在pg_database视图中记录了数据库中最旧事物的年龄。

  • 这个值是vacuum时更新上去的(包括autovacuum)

  • pg_database只记录了这个数据库中最旧的 年龄,没有记录是那张表

  • 表的年龄是记录在pg_class的relfrozenxid 字段中的

  • select relname,relfrozenxid,age(relfrozenxid)  from pg_class where relfrozenxid <>0 order  by age(relfrozenxid) desc limit 10;

  • lazy vacuum与 aggressive vacuum

  • vacuum如果每次全表扫描的进行垃圾回收,则 太消耗IO资源了,所以lazy vacuum上场了。

  • lazy vacuum不会全表扫描,之前做过垃圾回 收的数据块被记录的,再次vacuum时会跳过这些块。

  • 注意lazy vacuum不会回收事物ID。

  • lazy vacuum与 aggressive vacuum

  • 如果表的年龄没有超过 vacuum_freeze_table_age则会做lazy vacuum

  • vacuum_freeze_table_age默认是1亿5千万, autovacuum_freeze_max_age,默认是2亿, autovacuum_freeze_max_age >  vacuum_freeze_table_age

文档:postgresql autovacuum 4 怎么调整参数…
链接:http://note.youdao.com/noteshare?id=11fa73e4184f82af882eff10f8c0e0ca&sub=9CF85B6EF6E24FC6A034E6CC845A6631

  • 降低vacuum对生产性能影响

  • vacuum_cost_delay是指sleep的时间,默认值是0,建议设置为1ms

  • vacuum_cost_limit:当工作量到达一定是,就休眠一下,默认是 200

  • vacuum_cost_page_hit=1

  • vacuum_cost_page_miss=10

  • vacuum_cost_page_dirty=20

  • 即内存中整理一个块工作量为1,磁盘上整理一个块代价是10,如果是脏块是20

  • vacuum_cost_limit在SSD下建议设置10000,Raid盘的机械硬盘为1000

  • alter table percona.employee set (autovacuum_vacuum_cost_limit = 500);

调整autovacuum_vacuum_cost_limit(默认200)为1000,autovacuum_vacuum_cost_delay为10ms。

vacuum_cost_page_miss和vacuum_cost_page_dirty是否需要调小?

在VACUUM和ANALYZE命令的执行过程中,系统维持着一个内部计数器来跟踪各种被执行的I/O操作的估算开销。当累计的代价达到一个 限制(由vacuum_cost_limit指定),执行这些操作的进程将按照vacuum_cost_delay所指定的休眠一小段时间。然后它将重置计数器并 继续执行。

PG9.6及以上版本,调整old_snapshot_threshold(默认值为3h)避免长事物对vacuum影响, 调整前需要和开发运营评估是否有job或者 其他长事物,如果有需要确认长事物运行时间,根据具体情况设置,否则会有快照过旧的报错。

对于频繁update的表fillfactor建议调低60-70

vacuum慢

除了上面提到的参数外,还有当表特别大,索引太多等情况vacuum非常慢,有时候跑几个小时都没能跑完。

默认情况下数据库为了防止vacuum影响数据库性能,主要是io和cpu,所以设置了一些参数来降低对服务器负载的影响,当达到这个值的时候vacuum便会停止一段时间,然后继续工作

当表需要冻结的时候可能我们更想快速的跑完vacuum,这个时候可以对表进行调优,主要有以下参数:

vacuum_cost_page_hit:与vacuum工作的buffer有关,如果buffer不足则命中需要进行vacuum的页面成本就高

vacuum_cost_page_miss:与buffer有关,如果页面需要读入缓存中,但是没有空间,清理缓冲也需要成本,所以配置大的清理缓存是有必要的

vacuum_cost_page_dirty:操作页面时,需要对页面进行清理的工作的成本这里涉及的是IO的成本。

vaccum_cost_limit :这个是代价限制,前面的成本加起来如果超过了这个值,vacuum就停止工作,停止时间是由vacuum_cost_delay控制(默认0ms),当设置为0的时候自动停止延迟的设置

autovacuum_vacuum_cost_limit:默认值-1,则使用vaccum_cost_limit 的值

vacuum_cost_delay:vacuum停止工作时间

autovacuum_vacuum_cost_delay:默认2ms,自动清理的停止工作时间

postgres=# select name,setting,context from pg_settings where name like '%vacuum_cost%';
             name             | setting | context 
------------------------------+---------+---------
 autovacuum_vacuum_cost_delay | 2       | sighup
 autovacuum_vacuum_cost_limit | -1      | sighup
 vacuum_cost_delay            | 0       | user
 vacuum_cost_limit            | 200     | user
 vacuum_cost_page_dirty       | 20      | user
 vacuum_cost_page_hit         | 1       | user
 vacuum_cost_page_miss        | 10      | user
(7 rows)

所以我们可以增加vaccum_cost_limit以及减小vacuum_cost_delay,即增加清理的代价,减小停止时间来缩短vacuum的时间,只是数据库的负载以及cpu会增加,在特殊情况下可以进行此操作。

set  vacuum_cost_limit to 20000;

set vacuum_cost_delay to 1ms; 

如果我们要减小对系统负载,那么可以增加delay,并且减小vaccum_cost_limit代价:vacuum_cost_limit在SSD下建议设置10000,Raid盘的机械硬盘为1000

关于这块代价的计算参考:https://www.percona.com/blog/2018/08/10/tuning-autovacuum-in-postgresql-and-autovacuum-internals/

让我们想象一下 1 秒内会发生什么。(1 秒 = 1000 毫秒)

在读取延迟为 0 毫秒的最佳情况下,autovacuum 可以唤醒并进入睡眠 50 次(1000 毫秒/20 毫秒),因为唤醒之间的延迟需要为 20 毫秒。
1 second = 1000 milliseconds = 50 * autovacuum_vacuum_cost_delay
由于在 shared_buffers 中每次读取一页的相关成本为 1,因此每次唤醒可以读取 200 页,50 次唤醒可以读取 50*200 页。

如果在共享缓冲区中找到所有具有死元组的页面,autovacuum_vacuum_cost_delay 为 20ms,则它可以读取:
((200 /  vacuum_cost_page_hit) * 8) KB 在每一轮中需要等待autovacuum_vacuum_cost_delay的时间量。

因此,autovacuum 最多可以读取:每秒 50 * 200 * 8 KB = 78.13 MB(如果块已经在 shared_buffers 中找到),
考虑 block_size 为 8192 字节。

如果块不在共享缓冲区中并且需要从磁盘中获取,autovacuum 可以读取:50 * ((200 /  vacuum_cost_page_miss) * 8) KB = 7.81 MB/秒。

我们上面看到的所有信息都是针对read IO的。

现在,为了从页面/块中删除死元组,写操作的成本是:  vacuum_cost_page_dirty,默认设置为 20。

autovacuum 最多可以写/脏:50 * ((200 /  vacuum_cost_page_dirty) * 8) KB = 3.9 MB/秒。

通常,此成本平均分配给 autovacuum_max_workers 实例中运行的所有 autovacuum 进程数。因此,
增加 autovacuum_max_workers 可能会延迟当前正在运行的 autovacuum worker 的 autovacuum 执行。
并且增加 autovacuum_vacuum_cost_limit 可能会导致IO瓶颈。

解决

  • vacuum full,会锁表,不适合生成环境

  • pg_repack(pg_reorg项目的一个分支,但pg_reorg已停止更新),pg_squeeze

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论