清理过程

Mikage Yuziki 发布于 29 天前 63 次阅读


1 作用

1、移除死元组             

  • 删除死元组并对每个页面的活元组进行碎片整理(页内重排)。            
  • 删除指向死元组的索引元组。

2、冷冻老的Txid             

  • 必要时冻结老元组的Txid         
  • 更新冻结的与系统目录(pg_database和pg_class)相关的txid             
  • 如有可能,移除clog中不必要的部分

3、其他             

  • 更新已处理表的FSM和VM。             
  • 更新几个统计数据(pg_stat_all_tables等)。

2 分类

2.1 普通vacuum

(1) 从指定的表中获取每个表。             

(2) 获取表的ShareUpdateExclusiveLock锁。此锁允许读取其他事务。             

(3) 扫描所有页面以获取所有死元组,必要时冻结旧元组。             

(4) 如果存在,则移除指向相应死元组的索引元组。             

(5) 对表的每一页执行以下步骤(6)和(7)。             

(6) 移除死元组并重新分配页面中的活元组。             

(7) 更新目标表的相应FSM和VM。             

(8) 如果最后一页没有元组,则截断最后一页。             

(9) 更新与目标表的真空处理相关的统计信息和系统目录。             

(10) 更新与真空处理相关的统计数据和系统目录。            

(11) 如果可能的话,删除不必要的文件和clog的页面

清理过程会参考vm文件进行可见性判断,跳过不包含死元组的页,从而加速清理过程。

  • VM在9.6版中得到了增强,以提高冻结处理的效率
  • 新的VM显示页面可见性和关于在每个页面中元组是否冻结的信息

2.2 深度vacuum

Vacuum full:创建了新的数据文件,因此relfilenode 会改变,期间会阻塞表上的一切访问。

3 冻结过程

冻结极限txid
freezeLimit_txid=(OldestXmin−vacuum_freeze_min_age)

启动冻结处理时,PostgreSQL计算冻结限制txid,并冻结t_xmin小于冻结限制txid的元组。

其中“OldestXmin”是当前运行的事务中最早的txid。例如,如果在执行VACUUM命令时有三个事务(txids 100、101和102)正在运行,“OldestXmin”是100。如果当前没有其它事务, “OldestXmin”是执行此真空命令的txid。这里,vacuum_freeze_min_age是一个配置参数(默认值50000000)

模式触发时机
lazy普通 VACUUM(自动或手动)
只要 表的 relfrozenxid 还没超过 vacuum_freeze_table_age
eager当 表的 relfrozenxid ≥ vacuum_freeze_table_age
或 手动 VACUUM FREEZE

vacuum_freeze_table_age 默认 150 000 000(1.5 亿事务)

3.1 懒惰模式(Lazy Mode)

冻结过程会标记元组的t_infomask 标志位

懒惰模式会根据VM文件跳过 不包含死元组的页

3.2 急切模式(Eager Mode)

满足以下条件时,启动急切模式
pg_database.datfrozenxid<(OldestXmin−vacuum_freeze_table_age)

急切模式弥补了懒惰模式的缺陷。它根据VM文件进行判断,所有元组都已冻结的页面会被跳过,更新相关的系统目录,并在可能的情况下删除不必要的文件和clog页面。

Clog文件记录了事务的状态,可用于元组的可见性判断。已被冻结的元组都是可见的,因此就不需要保留相应的clog文件。

4 Autovacuum

自动清理过程,需要打开参数

autovacuum = on  # ( ON by default )
track_counts = on # ( ON by default )

4.1 触发条件

Autovacuum操作的实际内容:

1)vacuum;

2)Analyze

Autovacuum vacuum触发条件(如果由于更新和删除,表中的实际死元组数超过此有效阈值,则该表将成为autovacuum的候选表):

Autovacuum VACUUM thresold for a table = autovacuum_vacuum_scale_factor * number of tuples + autovacuum_vacuum_threshold

Autovacuum ANALYZE触发条件(自上次分析以来插入/删除/更新总数超过此阈值的任何表都有资格进行autovacuum分析)

Autovacuum ANALYZE threshold for a table = autovacuum_analyze_scale_factor * number of tuples + autovacuum_analyze_threshold

4.2 相关参数

 参数名                                           单位/默认             一句话作用                                         调优提示                                
 autovacuum                                bool/on           总开关;off=完全禁用后台自动清理                            生产必须 on,只临时排障可关                     
 autovacuum_max_workers                  int/3 ★           控制自动清理(autovacuum)进程的最大并发数                       CPU≥8 核可→6;IO 吃紧时先提 worker 再降 cost  
 autovacuum_naptime                       s/60 ★            两次清理操作之间的等待时间 ,控制 autovacuum 的运行频率。较短的时间间隔意味着 autovacuum 更频繁地运行。              大库可把 60→30 让“大表”更快被扫描到              
 autovacuum_vacuum_threshold             int/50 ★          触发 VACUUM 基础行数:deadtuple ≥ 50 + 0.2×reltuples 大表→0,靠比例即可                          
 autovacuum_vacuum_scale_factor         float/0.2 ★       与上行组成“20% 变化就扫”                  10 TB 级表→0.05 甚至 0.01,减少膨胀          
 autovacuum_analyze_threshold            int/50            触发 ANALYZE 基础行数                               同 threshold,通常同步调                   
 autovacuum_analyze_scale_factor        float/0.1         10% 变化就 ANALYZE                               可略低于 vacuum_factor                 
 autovacuum_vacuum_cost_limit           int/-1            本表 cost 上限;-1=取 vacuum_cost_limit           IO 慢盘→提高 200→1000                   
 autovacuum_vacuum_cost_delay           ms/-1             autovacuum 每次清理操作之间的延迟时间。它用于控制 autovacuum 的资源消耗,避免对生产环境造成过大压力。
较低的值意味着 autovacuum 在清理时会更积极地运行,但可能会对系统性能产生更大影响。;-1=取 vacuum_cost_delay          
 与 cost_limit 反比,可调 0 加速            
 autovacuum_freeze_max_age              xid/200 000 000 ★ 表 relfrozenxid 距当前 XID 超此值→强制 freeze          提前→150 000 000 减少 eager VACUUM      
 autovacuum_multixact_freeze_max_age   xid/400 000 000   MultiXact 版同上                                 通常同比例下调                             
 autovacuum_vacuum_insert_threshold     int/1000          ≥17 新增:INSERT 行数基础值                           _append-only 表→0 靠因子               
 autovacuum_vacuum_insert_scale_factor float/0.2         INSERT 20% 也 vacuum                           append-only→0.05 防新页膨胀              
 log_autovacuum_min_duration            ms/-1 ★           ≥此毫秒→日志;-1=不记,0=全记                            排障时→0,日常→250 ms                     
 vacuum_cost_limit                       int/200           控制 VACUUM 操作(清理和回收空间)的资源消耗限制。它定义了在每次 VACUUM 操作中,允许消耗的最大“成本”(以单位计)。成本是根据 I/O 和 CPU 消耗计算的。
较高的值允许 VACUUM 操作更积极地使用资源,而较低的值则限制其资源使用,以减少对生产环境的影响。                    
 与 autovacuum_vacuum_cost_limit 联动
 vacuum_cost_delay                       ms/0              vacuum 每次清理操作之间的延迟时间 IO 吃紧→2-10 ms,SSD→0                 
 vacuum_freeze_min_age                  xid/50 000 000    页级 freeze 最小 XID 年龄                           通常不动;调大→少写,调小→早 freeze              
 vacuum_freeze_table_age                xid/150 000 000 ★ 表级 eager freeze 阈值                            与 freeze_max_age 同步下调             
 vacuum_multixact_freeze_table_age     xid/150 000 000   MultiXact 版同上                                 同上                                  

使用口诀
worker+naptime 决定扫多快;factor+threshold 决定扫不扫;cost+delay 决定扫多猛;freeze_max_age 决定扫多狠;log_duration 看扫多慢。”

5 相关查询脚本

#查询pg_class.relfrozenxid和pg_database.datfrozenxid
SELECT n.nspname as "Schema", c.relname as "Name", c.relfrozenxid
             FROM pg_catalog.pg_class c
             LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
             WHERE c.relkind IN ('r','')
                   AND n.nspname <> 'information_schema' AND n.nspname !~ '^pg_toast'
                   AND pg_catalog.pg_table_is_visible(c.oid)
                   ORDER BY c.relfrozenxid::text::bigint DESC;


SELECT datname, datfrozenxid FROM pg_database WHERE datname = 'testdb';

#查询某张表的空闲空间比例
testdb=# CREATE EXTENSION pg_freespacemap;
CREATE EXTENSION
testdb=# SELECT count(*) as "number of pages",
       pg_size_pretty(cast(avg(avail) as bigint)) as "Av. freespace size",
       round(100 * avg(avail)/8192 ,2) as "Av. freespace ratio"
       FROM pg_freespace('accounts');

#查询检查指定表的每页的可用空间比率
SELECT *, round(100 * avail/8192 ,2) as "freespace ratio"
       FROM pg_freespace('accounts');

#查询表上的插入/删除/更新数
SELECT n_tup_ins as "inserts",n_tup_upd as "updates",n_tup_del as "deletes", n_live_tup as "live_tuples", n_dead_tup as "dead_tuples"
	 FROM pg_stat_user_tables
	 WHERE schemaname = 'scott' and relname = 'employee';