vacuum 与 autovacuum 以及事务回卷
  xwGmYGXf1w4S 2023年11月22日 31 0

1.vacuum 概念

vacuum用于回收死元组占用的存储空间。这些死元组是由于通过更新过期或者删除的元组不会从表中进行物理移除,直到执行一个 vacuum操作完成后才会被从表对应的物理文件中移除。因此在频繁更新的表上需要定期执行 vacuum操作。

vacuum执行操作,正常情况下,对dead tuple 仅仅进行收并不释放空间,以便空间可以重新使用而不需要再重新分配空间,如果带有 FULL 参数,那么回收dead tuple并释放空间。

2.vacuum 原理

对于不带有full参数的vacuum,仅仅将dead tuple从表对应的物理文件中标识移除,但是不释放空间。此刻,表对象数据文件依然使用同一个数据文件。

对于带有 full 参数的vacuum,该操作实际将表对应的物理文件内容重写到新的磁盘文件中,从而释放磁盘空间。

对于没有 full参数的vacuum操作,由于没有排他锁,表可以正常读取和写入对于有 full 参数的vacuum 操作,会获取表排他锁,因此比不带 full 参数要慢。

3.vacuum处理表的目的

·清除UPDATE或DELETE操作后留下的"死元组"

·跟踪表块中可用空间,更新free space map

·更新visibility map,index only scan以及后续vacuum都会利用到

·"冻结"表中的行,防止事务ID回卷

·配合ANALYZE,定期更新统计信息

4.事务回卷与冻结

冻结炸弹在PostgresQL又称为事务ID回卷,对应日志! Autovacuum appEars in postgresgl database: vacuum Xxoo.xxoo (to prevent wraparound)这个就是PostgresQL为了保证MVCC的一致性,再加上自身的实现机制,而必须要做的一项维护性操作。为了预防事务回卷,然后引入了freeze冻结这个操作,在PostgreSQL里面是一个很繁忙并且消耗资源的事情,大量的读IO(DATAFILE)和写I0 (DATAFILE以及XLOG,俗称“冻结炸弹”。

事物A xmin=8这个事物是它的过去,应该是可见,但是由于事物A到达圈的一个点画对角线之后,明明xmin=8是事物A的过去现在却变成了它的未来(不可见),事物冻结会把xmin=8的事物标记为xmin=2.xmin=2这个事物ID比谁都要老.可以理解成是所有事物的过去,并目在堆元祖的t infomask列标记为xmin frozen标记来标识冻结。

Xid是一个环循环2的32次方循环复用。

对于某一个xid来说左半边为过去,右半边为未来。

一个事务只能看到他过去的内容看不到未来

Xid=2为特殊事物id,他比所有事物id都要旧。

冻结时将元组的t infomask字段中的xmin frozen标记来标识冻结

t_xmin修改值为2为标识。

事务冻结1

为了保证同一个数据库中的最新和最旧的两个事务之间的年龄不超过2^31,PostgresQL引入了冻结(freeze功能。txid=2的事务在参与事务id比较时总是比所有事务都旧,冻结的txid始终处于非活跃状态,并且始丝对其他事务可见。

这里涉及到三个与冻结相关的参数:

1. vacuum freeze_min age

2.vacuum freeze table age

3.autovacuum freeze_max_age

还有涉及到的术语:

1.表年龄:当前事务号距上一次表执行freeze操作的事务id的差值

2.元组年龄:当前元组的xmin距上一次执行freeze操作的事务id的差值

9.4以后的版本,冻结后的元组是通过行上的infomask标志位实现的,由于PG的事务数量是有上限的,新老事务之间的年龄超过最大值之后必须要冻结,所以要及时监控,及时处理

监控数据库年龄,及时清理

select datname,age(datfrozenxid) from pg database order by 2 desc;自动处理机制

同一个数据库中,存在的最旧和最新两个事务之间的年龄最多是2^31,即20亿,当表的年龄大于autovacuum freeze_max age时(默认是2亿),autovacuum进程会自动对表进行freeze。

事务ID冻结 2

1. vacuum freeze min age

指定VACUUM在扫描表时用来决定是否冻结行版本的切断年龄(以事务计)。默认值是5千万个事务。尽管用户可以将这个值设置为从0到10亿,vacuum会悄悄地将有效值设置为autovacuum freeze max age值的一半,这样在强制执行的自动清理之间不会有过短的时间间隔。

2. vacuum freeze table age

当表的pg clas.relfrozenxid域达到该设置指定的年龄时,vacuum会执行-次激进的扫描。激进的扫描与常规ACUUM的不同在于它会访问每一个可能包含未冻结 XID 或者 MXID 的页面,而不只是那些可能包含死广元组的页面。默认值是15亿个事务。尽管用户可以把这个值设置为从0到20亿,vacuum会悄悄地将有效值设置为autovacuum freezemax age值的95%,因此在表上启动一次反回卷自动清理之前有机会进行一次定期手动vacuum。

3.autovacuum freeze max age

指定在一个vacuum操作被强制执行来防止表中事务ID回卷之前,一个表的pg class.relfrozenxid域能保持的最大年龄(事务的)。注意即便自动清理被禁用,系统也将发起自动清理进程来阻止回卷。

5.事务ID的分类

0~2是保留的txid,它们比任何普通txid都要旧

0: invalidtransactionid,表示无效的事务id

1: bootstraptransactionid,表示系统表初始化时的事务id,比任何普通的事务id都旧.

2: frozentransactionid,冻结的事务id,比任何普通的事务id都旧。 大于2的事务id都是普通的事务id,即从3开始就是普通的事务id。

6.autovacuum

什么时候会触发autovacuum

1、当 update.delete 的 tuples 数 量超过autovacuum_vacuum_scale_factor*table_size+autovacuum_vacuum_threshold

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

autovacuum优化

每个表dead tuples的数量(包括用户表和系统表)

pg_stat _all_tables.n_dead_tup 死亡行得到估计数量

# dead/live tuples在每个农中的比率

(n_dead_tup 死亡行得到估计数量/ n_live_tup 活着的行的估计数量)

#每一行的空间

(pg_class.relpages / pg_class.reltuples)

relpages

该表磁盘表示的尺寸,以页面计(页面尺寸为BLCKSZ)。这只是一个由规划器使用的估计值。它被VACUUM、ANALYZE以及一些DDL命(如CREATE INDEX) 所更新。

reltuples

表中的存活行数。这只是一个由规划器使用的估计值。它被VACUUM、ANALYZE以及一些DDL命令(如CREATEX所更新。

其中两个参数分别为:

autovacuum_vacuum_threshold = 50 #值

autovacuum_vacuum_scale_factor = 0.2#比例因子

死亡元组数可以认为是pg_stat_all_tables 中n_dead_tup的值由以上公式可以看出,一般在dead_tuple达到20%时,会进行自动清理,50行的闽值是为了防止非常频繁地清理微小的表。这个默认的比例比较适用于中小表,但如果表较大时,比如10GB大小的表,dead_tuple达到2GB时才清理这在清理的过程中会严重影响性能,一般来说解决方案有两种:

1.是调小大表的比例因子

2.是放弃比例因子,调大闯值

要注意在postgresql.conf中修改这些参数会产生全局影响,尤其调大阈值或调小比例因子会影响小表的清理,不过综合全局来看,可以忽略一此小表的清理问题。

比较理想的方案

在postgresql.conf中忽略比例因子,设置较大的阙值(例如设置autovacuum_vacuum_ scale_factor = 0和autovacuum_vacuum_threshold = 10000),然后根据各个表的delete和update频繁程度以及表的数据量单独为每个表设置闯值: ALTER TABLE test SET (autovacuum_vacuum_threshold = 100):

触发autovacuum的消耗

autovacuum的清理过程是从数据文件中读取页面(默认8KB数据块),并检查它是否需要清理,如果没有死亡元组,页面就会被丢弃而不做任何更改,否则它被清理(死元组被删除),被标记为“脏页”并最终写出来。成本核算基于postgresal.conf定义三个参数:

vacuum_cost_page_hit =1#如果页面是从shared buffers读取的,则计为1vacuum cost_page_miss = 10 #如果在shared buffers找不到并且需要从操作系统中读取,则计为10

vacuum_cost_page_dity = 20 #当清理修改一个之前净的块时需要花费的估计代价,它表示再次把脏块刷出到磁盘所需要的额外I/O,默认值为20

再加上另外两个参数即可计算出清理操作的成本:

autovacuum_vacuum_cost_delay =20ms #每次完成清理后睡眠20ms

autovacuum_vacuum_cost_limit = 200 #完成一次清理的消耗限制

惰性模式

惰性模式在开始冻结处理时,PG会计算freezelimit_txid,并冻结t_xmin小于freezelimit_txid的元组,freezelimit_txid= (oldestXmin-vacuum_freeze_min_age)

oldestXmin是当前正在运行的事务中最早的事务标识,vacuum_freeze_min_age是一个配置参数(默认为50000000)

vacuum_freeze_min_age

指定VACUUM在扫描表时用来决定是否冻结行版本的切断年龄(以事务计)。默认值是 5 千万个事务。尽管用户可以将这个值设置为从0到10亿,vacuum会悄悄地将有效值设置为autovacuum_freeze_max_age值的半,这样在强制执行的自动清理之间不会有过短的时间间隔。

vacuum_freeze_min_age

如果表的这个参数设置了,每次vacuum时候,行事务id大于这个数字的时候,都会被设置freeze

急切模式

普通的vacuum只会扫描脏页,而freeze操作会扫描所有可见且没有被全部冻结的页面,所以在每次vacuum时都去扫描是不合适的。

这时就有了急切冻结模式,急切冻结引入一个参数vacuum_freeze_table_age,同理该参数的最大值也只能是20亿,当表的年龄大于vacuum_freeze_table_age时,会执行急切冻结,表的年龄通过oldestxmin-pg_class.relfrozenxid计算得到,pg_classrelfrozenxid字段是在某个表被冻结后更新的,代表着某个表最近的冻结事务id。

而pg_database.relfrozenxid代表着当前库所有表的最小冻结标识,所以只有当该库具有最小冻结标识的表被冻结时,pg_database.relfrozenxid字段才会被更新。急切冻结的触发条件是pg_database.relfrozenxid<oldestxmin-vacuum_freeze_table_age,

这其实和上面的说法不冲突,因为某个数据库所有表中的最老的relfrozenxid就是数据库的relfrozenxid,所以冻结可以用一句话来理解:

当数据库中存在某个表的年龄大于vacuum_freeze_table_age参数设定值,就会执行急切冻结过程,当表中元组年龄超过vacuum_freeze_min_age,就可以被冻结,这里其实是必须和可以的区别。

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月22日 0

暂无评论

推荐阅读
  xaeiTka4h8LY   2024年05月17日   48   0   0 数据库JavaSQL
  xaeiTka4h8LY   2024年05月17日   44   0   0 数据库SQL
  xaeiTka4h8LY   2024年05月17日   36   0   0 MySQL数据库
xwGmYGXf1w4S