↑↓ 选择 ↵ 打开 ⌫ 改范围 完整检索页

pgsql.cc 提供对 postgresql.org 官网内容的中文翻译,由 Pigsty 团队维护。

测试与开发版本: 19 / devel
测试版PostgreSQL 19 RC 1 尚未正式发布,内容与行为在正式发布前可能变化。正式内容请看当前版本手册首页。

REPACK

REPACK — 重写表以回收磁盘空间

大纲

REPACK [ ( option [, ...] ) ] [ table_and_columns [ USING INDEX [ index_name ] ] ]
REPACK [ ( option [, ...] ) ] USING INDEX

其中 option 可以是以下之一:

    VERBOSE [ boolean ]
    ANALYZE [ boolean ]
    CONCURRENTLY [ boolean ]

而 table_and_columns 为:

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

描述

REPACK 可回收被死元组占用的存储空间。与 VACUUM 不同,它通过将 table_name 所指定表的全部内容重写到一个没有多余空间的新磁盘文件中来实现这一点(fillfactor 存储参数所保证的空间除外),从而允许未使用的空间返回给操作系统。

如果没有指定 table_name,REPACK 会处理当前数据库中当前用户具有 MAINTAIN 特权的每一张表和物化视图。此形式的 REPACK 不能在事务块中执行。如果使用了 CONCURRENTLY 选项,则也不允许使用这种形式。

如果指定了 USING INDEX 子句,行会根据索引中的信息在物理上重新排序。请参见下文的 Notes on Clustering。

当对一个表执行重整时,除非指定了 CONCURRENTLY 选项,否则会在该表上获取 ACCESS EXCLUSIVE 锁。有关该选项效果的讨论,请参见 Notes on Concurrent Operation。

参数

table_name #

表的名称(可以带模式限定)。

column_name #

要分析的特定列名称。默认是所有列。如果指定了列列表,则还必须同时指定 ANALYZE。

index_name #

索引的名称。

CONCURRENTLY #

允许其他事务在重整期间使用该表。更多详细信息请参见 Notes on Concurrent Operation。

VERBOSE #

以 INFO 级别打印每个表被重整时的进度报告。

ANALYZE
ANALYSE #

在重整表之后对该表执行 ANALYZE。目前这只在指定单个(非分区)表时受支持。此选项不能在事务块内使用,也不能从函数、过程或 DO 块中使用。

boolean #

指定所选选项应开启还是关闭。可以写 TRUE、ON 或 1 来启用该选项,也可以写 FALSE、OFF 或 0 来禁用它。boolean 值也可以省略,此时默认视为 TRUE。

说明

要重整表,必须对该表具有 MAINTAIN 特权。

REPACK 主要用于消除膨胀,并在使用 USING INDEX 时对表进行聚簇,以提高性能。虽然它也会推进表的 relfrozenxid 和 relminmxid,但它并不适合用来防止事务 ID 或多事务 ID 回卷:它会重写整个表及其所有索引,因此比 VACUUM 耗时长得多,而且可能在大部分工作完成后失败,尤其是在繁忙的表上使用 CONCURRENTLY 时。建议使用 VACUUM 来防止回卷(见第 24.1.5 节)。当表接近回卷时,VACUUM 会自行跳过索引清理(见 vacuum_failsafe_age),而其 INDEX_CLEANUP OFF 选项可以提前实现同样的效果。

REPACK 拒绝处理存在无效索引的表。用户必须事先删除或重建这些索引。

在 REPACK 运行期间,search_path 会临时改为 pg_catalog, pg_temp。

每个运行 REPACK 的后端都会在 pg_stat_progress_repack 视图中报告其进度。有关详细信息,请参见第 27.4.5 节。

重整分区表时,会重整其每个分区。如果指定了索引,则每个分区都会使用该索引的分区来重整。对分区表执行 REPACK 不能在事务块中进行。

聚簇说明

如果指定了 USING INDEX 子句,表中的行会按指定索引隐含的顺序在物理上重新排列,这称为聚簇。这可能影响性能:如果是在表内随机访问单行,那么表中数据的实际顺序并不重要。但如果更频繁地访问某些数据,并且有一个能把它们聚集在一起的索引,就能从聚簇中受益。如果从表中请求一个索引值范围,或者请求一个匹配多行的单个索引值,聚簇也会有所帮助,因为一旦索引确定了第一个匹配行所在的表页,其他匹配行很可能已经在同一个表页上,从而节省磁盘访问并加快查询。

对于 B-树索引,聚簇使用索引的线性排序顺序。其他支持聚簇的索引访问方法可能使用不同的排序策略,这种策略不一定对应任何 SQL 排序顺序。如果命令中指定了索引名,则使用该索引,并将其记录为该表的聚簇索引。(这同样适用于传给 CLUSTER 命令的索引。)如果未指定索引名,则使用已经被配置为聚簇索引的索引;如果没有这样的索引,就会报错。可以使用 ALTER TABLE ... CLUSTER ON 手工设置索引,也可以使用 ALTER TABLE ... SET WITHOUT CLUSTER 清除设置。

聚簇是一次性操作:表随后被更新时,这些更改不会继续保持聚簇。也就是说,并不会尝试根据聚簇顺序存储新行或更新后的行。(如果愿意,可以通过再次发出该命令来定期重新聚簇。此外,把表的 fillfactor 存储参数设置为低于 100%,有助于在更新期间保持聚簇顺序,因为如果页面上有足够空间,更新后的行会保留在同一页面上。)

在 B-树索引上聚簇时,REPACK 可以通过在指定索引上进行索引扫描,或者先执行顺序扫描再排序,来重写表。它会根据规划器代价参数和可用的统计信息,尝试选择速度更快的方法。在使用 B-树以外的访问方法的索引上聚簇时,REPACK 始终使用索引扫描。

因为规划器会记录有关表中数据顺序的统计信息,建议指定 ANALYZE 选项,或者在新近重整过的表上运行 ANALYZE。否则,规划器可能会选择很差的查询计划。

如果在 REPACK USING INDEX 中没有指定表名,那么所有已经定义了聚簇索引、且调用用户具有相应权限的表都会被处理。

资源说明

REPACK 会创建表及其所有索引的临时副本,因此需要至少等于表大小与索引大小之和的磁盘可用空间。

如果指定了 USING INDEX 子句,且规划器选择通过顺序扫描加排序来实现,那么除了上述空间之外,还需要一个最大可达表大小的临时排序文件。这种方法通常比索引扫描更快,但如果磁盘空间要求无法接受,可以通过临时将 enable_sort 设为 off 来禁用这种选择;如果不需要重新排序表,也可以省略 USING INDEX。

就事务 ID 回卷而言(见第 24.1.5 节),REPACK 被视为一个长时间运行的事务,会阻止 VACUUM 清理其他表中的死行。在重整非常大的表时,建议监控运行 REPACK 的进程的 pg_stat_activity.backend_xid,以免导致其他表过度膨胀。

建议在重整之前,将 maintenance_work_mem 设置为一个相当大的值(但不要超过你可为 REPACK 操作预留的内存总量)。

并发操作说明

警告

带 CONCURRENTLY 选项的 REPACK 不是 MVCC 安全的。重整事务提交后,如果并发事务使用的是重整开始之前取得的快照,该表在这些事务看来将是空的。更多详细信息请参见第 13.6 节。

REPACK 会把表内容(忽略死元组)复制到一个新文件中,并按指定索引排序,同时还会为每个索引创建一个新文件。然后它交换表及其所有索引的旧文件和新文件,并删除旧文件。不使用 CONCURRENTLY 选项时,会在开始时获取 ACCESS EXCLUSIVE 锁,并在整个操作期间持续持有,以确保处理过程中旧文件不会变化;否则,任何并发更改都会因交换而丢失。

相比之下,指定 CONCURRENTLY 选项时,大部分操作在持有 SHARE UPDATE EXCLUSIVE 锁的情况下执行,只有在交换表文件和索引文件的最后阶段才会获取 ACCESS EXCLUSIVE 锁。在创建新表和索引文件期间发生的数据更改会通过逻辑解码(见第 47 章)捕获,并在请求 ACCESS EXCLUSIVE 锁之前应用。获取该锁后,会对捕获的剩余并发数据更改做最后一轮处理,然后交换文件。因此,该锁通常只会持有很短的时间,具体取决于等待锁期间积累的更改量。

处理这些并发更改时,每个被并发更新或删除的元组都需要占用少量固定大小的内存,这部分内存不受 maintenance_work_mem 限制;如果在 REPACK 执行期间被并发更新或删除的行超过大约 1.04 亿,命令就会失败。此外,所有并发更改的完整元组数据(未被 UPDATE 修改的 TOAST 列除外)都会单独存储在磁盘上。

如果其他事务在重整期间对该表执行 DDL 命令,REPACK (CONCURRENTLY) 可能无法完成。

REPACK (CONCURRENTLY) USING INDEX 不会尝试对重整开始之后插入到表中的行进行排序。

CONCURRENTLY 选项不能在以下情况下使用:

  • 该关系不是表(例如,它是物化视图)。

  • 该表是 UNLOGGED。

  • 该表是分区表。

  • 该表没有主键,也没有基于索引的复制标识。

  • 该表是系统目录或 TOAST 表。

  • 表的访问方法不是 heap。

  • 表通过 user_catalog_table 存储参数声明为目录表。

  • REPACK 在事务块中执行。

  • max_repack_replication_slots 配置参数不允许再创建一个额外的复制槽。

示例

重整表 employees:

REPACK employees;

基于其索引 employees_ind 重整表 employees(由于指定了索引,这实际上就是聚簇):

REPACK employees USING INDEX employees_ind;

以并发模式使用与之前相同的索引重整 employees 表:

REPACK (CONCURRENTLY) employees USING INDEX;

按物理顺序重整表 cases,在重整完成后对给定列执行 ANALYZE,并显示信息消息:

REPACK (ANALYZE, VERBOSE) cases (district, case_nr);

重整数据库中所有你具有 MAINTAIN 特权的表:

REPACK;

重整所有此前已配置聚簇索引、且你具有 MAINTAIN 特权的表,并显示信息消息:

REPACK (VERBOSE) USING INDEX;

兼容性

SQL 标准中没有 REPACK 语句。

提交更正

译文有误、术语不当或页面显示问题,请到译文仓库 pgsty/pgdoc 报告译文问题。英文原文本身的问题,请在当前版本的对应页面向上游反馈;上游会在正式发布前持续修订。