vacuum是一种维护过程,有助于PostgreSQL的持久运行,它的两个主要任务是删除死元组,以及冻结事务标识。
vacuum的作用:
1.释放,再利用更新/删除的行所占据的磁盘空间。
2.更新postgresql查询计划中使用的统计数据。
3.防止因事务ID的重置而使非常老的数据丢失。
并发清理过程为指定的表或者数据库中的表执行以下任务:
第一部分
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)
清理过程会使用共享缓存,处理过的页面不会缓存在共享缓存里。
为了移除死亡元组,清理过程有两种模式,分别是并发清理与完整清理。清理过程删除表文件每个页面中的死元组,而其他事务可以在其运行时继续读取该表。相反,完整清理不仅会 移除整个文件中所有的死元组,还会对整个文件中所有的活元组进行碎片整理。其他事务在完整清理运行时无法访问该表。
清理过程涉及全表扫描,所以引入了可见性映射(Visibility Map,VM)来提高移除死元组的效率。
VM:每个表都有各自的可见性映射,用于保存表文件中每个页面的可见性,页面的可见性确定了每个页面是否包含死亡元组。清理过程可以跳过没有死亡元组的页面。
如图,假设一个表包含三个页面,第0号页和第2号页包含死亡元组,但是第一号页不包含死亡元组,在可见性映射中保存着包含死亡元组的信息,则在清理过程中,会参考VM,跳过第一个页面清理下边的页面。
每个VM 文件后缀_vm保存,如一个表文件的relfilenode是2612,则VM文件为2612_vm,VM在9.6版本做了加强,除了显示页面可见性,还包含了页面中原则是否全部冻结的信息。
冻结过程分两种模式:惰性模式和迫切模式。
惰性模式在开始冻结处理时,PG会计算freezelimit_txid,并冻结t_xmin小于freezelimit_txid的元组,
freezelimit_txid=(OldestXmin-vacuum_freeze_min_age)
OldestXmin是当前正在运行的事务中最早的事务标识,vacuum_freeze_min_age是一个配置参数(默认为50000000)
迫切模式 9.5版本之前会扫描所有页以检查表中的所有元组,更新相关的系统目录,并在可能的情况下删除不必要的文件和clog页。
满足pg_database.datfrozenxid <(OldestXmin-vacuum_freeze_table_age)时,会触发迫切模式,vacuum_freeze_table_age 默认值为150000000。
冻结每个表后,目标表的pg_class.relfrozenxid被更新。 在完成vacuum处理之前,必要时更新pg_database.datfrozenxid。每个pg_database.datfrozenxid列在相应的数据库中保存最小pg_class.relfrozenxid。
9.6版本进行了VM与冻结过程,新VM包含了每个页面中所有元组是否被全部冻结的信息,在迫切模式下进行冻结处理时,可以跳过仅包含冻结元组的页面。
带有freeze的vacuum会强制冻结指定表中的所有事务标识,而且freezeLimit会被设置为OldestXmin而不是OldestXmin – vacuum_freezre_min_age。
原创文章,作者:奋斗,如若转载,请注明出处:https://blog.ytso.com/237288.html