PostgresSQL的MVCC与VACUUM


  首先我们知道Oracle、MySQL的MVCC(多版本并发控制)是通过undo日志去完成的;但是 PostgreSQL中是没有UNDO的,老版本的数据元组直接存放在数据页面中,这样带来的问题就是旧元组需要不断地进行清理以释放空间,这也是数据库膨胀的根本原因

一、PostgreSQL的数据结构

1.1 数据页结构

  • header data:数据头是page生成的时候随之产生的,由pageHeaderData定义结构,24个字节长,包含了page的相关信息,下面是数据结构:
    • pd_lsn: 存储最近改变该页面的xlog位置。
    • pd_checksum:存储页面校验和。
    • pdlower,pdupper:pdlower指向行指针(line pointer)的尾部,pdupper指向最后那个元组。
    • pd_special: 索引页面中使用,它指向特殊空间的开头。
  • line pointer:行指针,四字节,每一条元组会有一个行指针指向真实元组位置。
  • heap tuple:存放真实的元组数据,注意元组是从页面的尾部向前堆积的,元组和行指针之间的是数据页的空闲空间。

1.2 元组结构

  元组,也叫tuple,其实就是数据库中的一条记录。

  • tableoid,这一行所属表的OID。
  • xmin,插入该行版本的事务身份(事务ID)。
  • cmin,插入事务中的命令标识符(从0开始)。
  • xmax,更新、删除事务的事务ID
  • cmax,删除事务中的命令标识符,或者为0。
  • ctid,行版本在其表中的物理位置。

二、PostgreSQL的MVCC

  在PostgreSQL中,当一行记录被更新时,该行数据的新版本将被创建并插入表中,之前版本提供一个指针指向新版本,之前版本被标记为"expired"过期,但是还保留在数据库直到垃圾收集器回收掉。、
  事务状态是保存在 $PGDATA/pg_xact中. 这个表包含每个事务状态信息的两个字节,可能的状态有in-progress, committed, 或aborted。 当一个事务结束后,PostgreSQL并不会将数据库记录的改变undo回滚的,它只是标记事务为aborted。一个PostgreSQL表可能包含许多这样aborted退出事务的数据。

三、PostgreSQL的清理机制

  PostgreSQL使用Vacuum来做垃圾回收。Vacuum 清理器会删除expired过期/aborted退出的记录版本以及被垃圾回收的tuple相关的索引项。
  VACUUM类似于MySQL的purge线程,而在PG中,对应的就是vacuum。

#基本语法
VACUUM [ ( option [, ...] ) ] [ table_and_columns [, ...] ]
VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] [ ANALYZE ] [ table_and_columns [, ...] ]

其中option可以是下列之一:

FULL
选择“完全”清理,它可以收回更多空间,并且需要更长时间和表上的排他锁。这种方法还需要额外的磁盘空间,因为它会创建该表的一个新拷贝,并且在操作完成之前都不会释放旧的拷贝。通常这种方法只用于需要从表中收回数量庞大的空间时。

FREEZE
选择激进的元组“冻结”。指定FREEZE 等价于参数vacuum_freeze_min_age和 vacuum_freeze_table_age设置为0的 VACUUM。当表被重写时总是会执行激进的冻结, 因此指定FULL时这个选项是多余的。

VERBOSE
为每个表打印一份详细的清理活动报告。

ANALYZE
更新优化器用以决定最有效执行一个查询的方法的统计信息。

DISABLE_PAGE_SKIPPING
通常,VACUUM将基于可见性映射跳过页面。已知所有元组都被冻结的页面总是会被跳过,而那些所有元组对所有事务都可见的页面则可能会被跳过(除非执行的是激进的清理)。此外,除非在执行激进的清理时,一些页面也可能会被跳过,这样可避免等待其他页面完成对其使用。这个选项禁用所有的跳过页面的行为,其意图是只在可见性映射内容被怀疑时使用,这种情况只有在硬件或者软件问题导致数据库损坏时才会发生。

table_name
要清理的表或物化视图的名称(可以有模式修饰)。如果指定的表示一个分区表,则它所有的叶子分区也会被清理。

column_name
要分析的指定列的名称。缺省是所有列。如果指定了一个列的列表,则ANALYZE也必须被指定。

而table_and_columns是:

    table_name [ ( column_name [, ...] ) ]

  简单的 VACUUM(不带FULL)简单地收回空间并使其可以被重用。这种形式的命令可以和表的普通读写操作并行,因为它不会获得一个排他锁。但是,这种形式中额外的空间并没有被还给操作系统(在大多数情况下),它仅仅被保留在同一个表中以备重用。VACUUM FULL将表的整个内容重写到一个新的磁盘文件中,并且不包含额外的空间,这使得没有被使用的空间被还给操作系统。这种形式的命令更慢并且在其被处理时要求在每个表上保持一个排他锁。
输出
如果声明了VERBOSE,VACUUM会发出进度消息来表明当前正在处理哪个表。各种有关这些表的统计信息也会打印出来。

注意

  • 要清理一个表,通常必须是表的拥有者或者超级用户。但是,数据库拥有者被允许清理他们的数据库中除了共享目录之外的所有表(对于共享目录的限制意味着一个真正的数据库范围的VACUUM只能被超级用户执行)。VACUUM将会跳过执行者不具备清理权限的表。
  • VACUUM不能在一个事务块内被执行。
  • 建议经常清理活动的生产数据库(至少每晚一次),以保证移除失效的行。在增加或删除了大量行之后, 对受影响的表执行VACUUM ANALYZE命令是一个很好的做法。这样做将把最近的更改更新到系统目录,并且允许 PostgreSQL查询规划器在规划用户查询时做出更好的选择。
  • 日常使用时,不推荐FULL选项,但在特殊情况时它会有用。一个例子是当你删除或者更新了一个表中的绝大部分行时,如果你希望在物理上收缩表以减少磁盘空间占用并且允许更快的表扫描,则该选项是比较合适的。VACUUM FULL通常会比简单VACUUM更多地收缩表。
  • VACUUM会导致I/O流量的大幅度增加,这可能导致其他活动会话性能变差。因此,有时建议使用基于代价的清理延迟特性。

猜你喜欢

转载自blog.csdn.net/qq_42979842/article/details/108504680