【读书笔记】Postgresql清理过程

6.清理过程

清理过程(通常简称为VACUUM)是一种维护过程,有助于PostgreSQL的持久运行。它的两个主要任务是删除死元组,以及冻结事务标识。

为了移除死元组,清理过程有两种模式,分别是并发清理与完整清理。清理过程会删除表文件每个页面中的死元组,而其他事务可以在其运行时继续读取该表。相反,完整清理不仅会移除整个文件中所有的死元组,还会对整个文件中所有的活元组进行碎片整理。其他事务在完整清理运行时无法访问该表。

并发清理概述

清理过程为指定的表或数据库中的所有表执行以下任务

  • 移除死元组:移除每一页死元组及索引元组,并对每一页活的额进行碎片整理
  • 冻结旧的事务标识:如有必要冻结旧元组的事务标识;更新与冻结事务标识相关的视图(pg_database/pg_class);如果可能,移除不必要的提交日志文件
  • 其他:更新已处理表的空闲空间映射(FSM)和可见性映射(vm)

伪代码,并行清理

(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

第一部分

这一部分执行冻结处理,并删除指向死元组的索引元组。

首先,PostgreSQL扫描目标表以构建死元组列表,如果可能的话,还会冻结旧元组。该列表存储在本地内存中的 maintenance_work_mem 里(维护用的工作内存)。

在11.0或更高版本中,如果目标索引是B树,是否执行清除阶段由配置参数vacuum_cleanup_index_scale_factor决定。

当maintenance_work_mem已满,且未完成全部扫描时,PostgreSQL继续进行后续任务,即步骤(4)到(7),完成后再重新返回步骤(3)并继续扫描。

第二部分

这一部分会移除死元组,并逐页更新FSM和VM。非必要的行指针是不会被移除的,它们会在将来被重用。因为如果移除了行指针,就必须同时更新所有相关索引中的索引元组。

第三部分

第三部分会针对每个表,更新与清理过程相关的统计信息和系统视图。

后续处理

当处理完成后,PostgreSQL会更新与清理过程相关的几个统计数据,以及相关的系统视图;如果可能的话,它还会移除部分不必要的CLOG文件。

6.2可见性映射

VM 的基本概念很简单。每个表都拥有各自的可见性映射,用于保存表文件中每个页面的可见性。页面的可见性确定了每个页面是否包含死元组。清理过程可以跳过没有死元组的页面。

每个 VM 由一个或多个 8 KB 页面组成,文件以后缀_vm 保存。例如,一个表文件的relfilenode是18751,其FSM(18751_fsm)和VM(18751_vm)。

6.3冻结过程

冻结过程有两种模式,以特定条件选择,分别为惰性模式和迫切模式。并发清理通常被称为“惰性清理”。 本文中定义的惰性模式是冻结过程执行的模式。

PG在这里,引入了名为”冻结”的概念:当重置的时候,会对当前所有数据表的行进行一遍冻结标,设置其为可以对任意事务可见.这样,重置事务id之后,如果新的事务访问到这个表,就直接可以访问到所有需要的数据了

惰性模式

当开始冻结处理时, PostgreSQL 计算 freezeLimit_txid ,并冻结 t_xmin 小于freezeLimit_txid的元组。

freezeLimit_txid定义如下:

freezeLimit_txid=(OldestXmin-vacuum_freeze_min_age)

OldestXmin 是当前正在运行的事务中最早的事务标识.里vacuum_freeze_min_age是一个配置参数(默认值为50 000 000)

迫切模式

会扫描所有页面,检查表中的所有元组,更新相关的系统视图,并在可能时删除不必要的CLOG文件与页面。当满足以下条件时,会执行迫切模式:

pg_database.datfrozenxid<(OldestXmin-vacuum_freeze_table_age)

pg_database.datfrozenxid 是系统视图pg_database 中的列,并保存着每个数据库中最老的已冻结的事务标识。vacuum_freeze_table_age是配置参数(默认为150 000000)

改进迫切模式中的冻结过程

9.5或更低版本中的迫切模式效率不高,因为它始终会扫描所有页面。新版本中新VM包含着每个页面中所有元组是否都已被冻结的信息。在迫切模式下进行冻结处理时,可以跳过仅包含冻结元组的页面。

6.4移除不必要的CLOB文件

当更新pg_database.datfrozenxid时, PostgreSQL会尝试删除不必要的CLOG文件。注意,相应的CLOG页面也会被删除。

mydb=# select datname,datfrozenxid from pg_database;
  datname  | datfrozenxid 
-----------+--------------
 mydb      |          548
 mytestdb  |          548
 postgres  |          548
 template0 |         1725
 template1 |          548
 kettledb  |          548
 kettlejob |          548
(7 rows)
[postgres@pgtest ~]$ ls -la -h /pgdata/data13/pg_xact/
total 16K
drwx------  2 postgres postgres 4.0K Jan 19 14:05 .
drwx------ 19 postgres postgres 4.0K Jan 19 14:09 ..
-rw-------  1 postgres postgres 8.0K Jan 20 17:34 0000

6.5自动清理守护进程

动清理守护程序周期性地唤起几个autovacuum_worker进程,默认情况下每分钟唤醒一次(由参数autovacuum_naptime定义),每次唤起三个工作进程(由autovacuum_max_works定义。

6.6完整清理

删除部分数据,无法释放真正的页表的空间

完整清理模式

  • 1.创建新的表文件:当对表执行vacuum full时,pg首先获取表上的AccessExclusiveLock锁,并创建8kb新表文件,该锁不允许其他的任何访问
  • 2.将活元组复制到新表
  • 3.删除旧文件,重建索引并更新统计信息FSM和VM

伪代码:

(1)  FOR each table
(2)       Acquire AccessExclusiveLock lock for the table
(3)       Create a new table file
(4)       FOR each live tuple in the old table
(5)            Copy the live tuple to the new table file
(6)            Freeze the tuple IF necessary
            END FOR
(7)        Remove the old table file
(8)        Rebuild all indexes
(9)        Update FSM and VM
(10)      Update statistics
            Release AccessExclusiveLock lock
       END FOR

注意:当执行vacuum full命令时,没有人可以访问该表;最多会临时使用两倍的于表的磁盘空间。

通过pg_freespacemap 检查空间空闲率

CREATE EXTENSION pg_freespacemap;
--查询表文件空闲率
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('tb1_b');
--查询每页空闲率
SELECT *, round(100 * avail/8192 ,2) as "freespace ratio"
                FROM pg_freespace('tb1_b');
--eg exec vacuum
mydb=# delete from tb1_b where id % 10 !=0;
DELETE 4500
mydb=# 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('tb1_b');
 number of pages | Av. freespace size | Av. freespace ratio 
-----------------+--------------------+---------------------
              23 | 0 bytes            |                0.00
(1 row)
mydb=# vacuum tb1_b;
VACUUM
mydb=# 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('tb1_b');
 number of pages | Av. freespace size | Av. freespace ratio 
-----------------+--------------------+---------------------
              23 | 6571 bytes         |               80.21
(1 row)
--eg exec vacuum full
mydb=# SELECT *, round(100 * avail/8192 ,2) as "freespace ratio"
mydb-#                 FROM pg_freespace('tb1_b');
 blkno | avail | freespace ratio 
-------+-------+-----------------
     0 |  6528 |           79.00
     1 |  6496 |           79.00
     2 |  6528 |           79.00
     3 |  6496 |           79.00
     4 |  6496 |           79.00
     5 |  6528 |           79.00
     6 |  6496 |           79.00
     7 |  6528 |           79.00
     8 |  6496 |           79.00
     9 |  6496 |           79.00
    10 |  6528 |           79.00
    11 |  6496 |           79.00
    12 |  6528 |           79.00
    13 |  6496 |           79.00
    14 |  6496 |           79.00
    15 |  6528 |           79.00
    16 |  6496 |           79.00
    17 |  6528 |           79.00
    18 |  6496 |           79.00
    19 |  6496 |           79.00
    20 |  6528 |           79.00
    21 |  6496 |           79.00
    22 |  7936 |           96.00
(23 rows)
mydb=# vacuum full tb1_b;
VACUUM
mydb=# SELECT *, round(100 * avail/8192 ,2) as "freespace ratio"
                FROM pg_freespace('tb1_b');
 blkno | avail | freespace ratio 
-------+-------+-----------------
     0 |     0 |            0.00
     1 |     0 |            0.00
     2 |     0 |            0.00
(3 rows)

本节英文版

https://www.interdb.jp/pg/pgsql06.html

请使用浏览器的分享功能分享到微信等