PostgreSQL之Concurrent VACUUM

在前面一文PostgreSQL之tuple结构中我们初步了解到PG中是采用了MVCC多版本机制,数据在删除时并不会直接从页面中删除掉而是通过修改t_xmax将其标记为delete,更新数据也是通过把原数据标记为delete并插入一条新记录的形式。
既然老的数据不会立马删除,那数据库一定就得一些其他的机制可以进行异步的清理,否则数据库长久肯定会有数据膨胀的问题存在。PG中为了清理老的无用数据,引入了VACUMM机制。
VACUUM有两种方式:Concurrent VACUUM及Full VACUUM。Concurrent VACUUM也简称为VACUUM,它允许在执行期间同时可以并行对所操作的表进行读取操作,主要工作是移除过期的tuples数据。相反,Full VACUUM不允许在执行期间访问所操作的表,主要工作是移除过期的tuples数据并整理整个文件中的活跃tuples。
本文我们主要了解Concurrent VACUUM的操作内容及步骤。整个上,Concurrent VACUUM包含三部分的内容:

  • 移除过期tuples,包括移除过期的表中的tuple及对应的index中的tuple。
  • 冻结老的txids,包括冻结老的txids、更新冻结txid相关的系统表、移除clog中不必要的问题。
  • 其它,包括更新已处理表的FSM(上文提到过,即Free Space Map)及VM(Visibility Map,在PG8.4版本中引入用来提升vacuum性能)、更新相关的统计信息。

Concurrent VACUUM操作步骤的伪代码过程如下,

(1)  FOR each table
(2)       Acquire ShareUpdateExclusiveLock lock for the target table

          /* The first block */
(3)       Scan all pages to get all dead tuples, and freeze old tuples if necessary 
(4)       Remove the index tuples that point to the respective dead tuples if exists

          /* The second block */
(5)       FOR each page of the table
(6)            Remove the dead tuples, and Reallocate the live tuples in the page
(7)            Update FSM and VM
           END FOR

          /* The third block */
(8)       Clean up indexes
(9)       Truncate the last page if possible
(10       Update both the statistics and system catalogs of the target table
           Release ShareUpdateExclusiveLock lock
       END FOR

        /* Post-processing */
(11)  Update statistics and system catalogs
(12)  Remove both unnecessary files and pages of the clog if possible

注:在PG13版本中,VACUUM支持PARALLEL参数,使用PARALLEL参数时可以让vacuum index动作和clean up index动作并行执行。

可以发现,Concurrent VACUUM整体步骤包括一个FOR循环操作及一些后续操作。FOR循环操作中主要包括三块步骤。下面具体了解一下三个步骤的具体内容。

FOR循环步骤一

主要工作内容为冻结操作及删除指向过期tuples的index tuples信息。
首先扫描目标表,生成过期tuples列表并进行冻结操作。过期列表保存在本地内存中的maintenance_work_mem。冻结操作后面进行描述。
上述完成后,通过过期tuples列表移除对应的index tuples,此过程又被称为"cleanup stage"。在PG 10或更早版本中,此步骤一直会执行。在PG 11及以后,如果目标index是B-tree索引,是否执行取决于参数vacuum_cleanup_index_scale_factor。
如果aintenance_work_mem内存满了而且执行未结束,PG将会跳过步骤(3),然后进行步骤(4)到步骤(7),然后再返回到步骤(3)进行后面的扫描操作。

FOR循环步骤二

主要工作内容为一个页面一个页面的删除其中过期的表tuples及更新FSM及VM。本身也是一个FOR循环。
PostgreSQL之Concurrent VACUUM_第1张图片上图描述一个表中第一个页面有3个Tuple,其中Tuple 2为过期数据,因此这个步骤中会移除Tuple 2并且对剩余的tuples重新排序,之后更新这个页面的FSM及VM。
注:这里并不会删除多余的line pointer 2,它会在后面被重用。如果删除了这个line pointer 2,相关索引所有的index tuples都需要被更新。

FOR循环步骤三

主要工作内容为cleanup相关indexes,以及更新VACUUM相关表的统计信息及系统元数据信息。
另外,如果最后一个页面没有tuple,这个页面就会被truncate掉。

后续处理

上述操作完成后,PG会更新多个与vacuum操作有关的统计信息及系统元数据信息,如果可能的话也会移除clog中不必要的部分。

关于VM(Visibility Map)

上面提到VACUUM中有一个步骤是更新VM,那么VM是什么呢?
VM是PG为了优化VACUUM效率在8.4版本中引入的功能,其主要是用来保存每个表对应的页面的可见性。页面可见性表示这个页面中是不是有过期tuples。利用VM,VACUUM过程可以直接跳过没有过期tuples的页。
PostgreSQL之Concurrent VACUUM_第2张图片
上图示例说明VM是怎么用的,假设一个表有三个页面,其中第0个页面和第2个页面中有过期tuples但第1个页面中没有过期tuples,这样VACUUM进程就会根据VM直接跳过第1个页面。一个VM里面记录1或多个页面的信息,VM文件后缀是vm,比如一个表的文件内容如下:

$ cd $PGDATA
$ ls -la base/16384/18751*
-rw------- 1 postgres postgres  8192 Apr 21 10:21 base/16384/18751
-rw------- 1 postgres postgres 24576 Apr 21 10:18 base/16384/18751_fsm
-rw------- 1 postgres postgres  8192 Apr 21 10:18 base/16384/18751_vm

你可能感兴趣的:(Postgresql,数据库,postgresql,数据库)