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 子句,行会根据索引中的信息在物理上重新排序。有关聚簇的说明,请参见下文注记。
当对一个表执行重整时,会在该表上获取 ACCESS EXCLUSIVE 锁。这会阻止任何其他数据库操作(包括读和写)在
REPACK 完成前访问该表。如果希望在重整期间保持该表可访问,可以考虑使用 CONCURRENTLY 选项。
聚簇说明
如果指定了 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 中没有指定表名,那么所有已经定义了聚簇索引、且调用用户具有相应权限的表都会被处理。
资源说明
当使用索引扫描,或者使用不带排序的顺序扫描时,会创建一个临时副本,其中包含按索引顺序排列的表数据。同时也会为表上的每个索引创建临时副本。因此,你需要至少等于表大小与索引大小之和的磁盘可用空间。
当使用顺序扫描加排序时,还会创建一个临时排序文件,因此峰值临时空间需求最多可达到表大小的两倍,再加上索引大小。这种方法通常比索引扫描方法更快,但如果磁盘空间要求无法接受,可以通过临时将 enable_sort 设为 off 来禁用这种选择。
建议在重整之前,将 maintenance_work_mem 设置为一个相当大的值(但不要超过你可为 REPACK 操作预留的内存总量)。
参数
table_name#表的名称(可以带模式限定)。
column_name#要分析的特定列名称。默认是所有列。如果指定了列列表,则还必须同时指定
ANALYZE。index_name#索引的名称。
CONCURRENTLY#允许其他事务在重整期间使用该表。
在内部,
REPACK会把表内容(忽略死元组)复制到一个新文件中,并按指定索引排序,同时还会为每个索引创建一个新文件。然后它交换表及其所有索引的旧文件和新文件,并删除旧文件。需要ACCESS EXCLUSIVE锁,以确保处理过程中旧文件不会变化,因为这些变化会因交换而丢失。使用
CONCURRENTLY选项时,ACCESS EXCLUSIVE锁只会在交换表文件和索引文件时获取。在创建新表和索引文件期间发生的数据更改会通过逻辑解码(第 47 章)捕获,并在请求ACCESS EXCLUSIVE锁之前应用。因此,该锁通常只会持有完成文件交换所需的时间,应该相当短。不过,如果REPACK等待锁期间对表做了太多数据更改,那么在持有ACCESS EXCLUSIVE锁时,必须先处理这些更改,然后才能交换文件,因此这段时间仍可能比较明显。请注意,带
CONCURRENTLY选项的REPACK不会尝试重新排列在重整开始之后插入到表中的行。还要注意,REPACK可能会因为其他事务在重整期间对该表执行的 DDL 命令而无法完成。注意
除了 Notes on Resources 中解释的临时空间要求之外,
CONCURRENTLY选项还可能会稍微增加一些临时空间的使用。原因是,其他事务可以执行 DML 操作,而这些操作在REPACK复制完旧文件中的所有现有元组之前无法应用到新文件中。因此,在复制期间插入到旧文件中的那些元组也会被单独存储在一个临时文件中,直到它们能够被处理为止。CONCURRENTLY选项不能在以下情况下使用:该关系不是表(例如,它是物化视图)。
该表是
UNLOGGED。该表是分区表。
该表没有主键,也没有基于索引的复制标识。
该表是系统目录或 TOAST 表。
表的访问方法不是
heap。表通过
user_catalog_table存储参数声明为目录表。REPACK在事务块中执行。max_repack_replication_slots配置参数不允许再创建一个额外的复制槽。
警告
带
CONCURRENTLY选项的REPACK不是 MVCC 安全的,详见第 13.6 节。VERBOSE#以
INFO级别打印每个表被重整时的进度报告。ANALYZEANALYSE#在重整表之后对该表执行 ANALYZE。目前这只在指定单个(非分区)表时受支持。此选项不能在事务块内使用,也不能从函数、过程或
DO块中使用。boolean#指定所选选项应开启还是关闭。可以写
TRUE、ON或1来启用该选项,也可以写FALSE、OFF或0来禁用它。boolean值也可以省略,此时默认视为TRUE。
说明
要重整表,必须对该表具有 MAINTAIN 特权。
REPACK 拒绝处理存在无效索引的表。用户必须事先删除或重建这些索引。
在 REPACK 运行期间,search_path 会临时改为 pg_catalog, pg_temp。
每个运行 REPACK 的后端都会在 pg_stat_progress_repack 视图中报告其进度。有关详细信息,请参见第 27.4.5 节。
重整分区表时,会重整其每个分区。如果指定了索引,则每个分区都会使用该索引的分区来重整。对分区表执行 REPACK 不能在事务块中进行。
示例
重整表 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 语句。