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 子句,表中的行会按该索引指定的顺序存储;由于行会随后在物理上聚簇,因此称为 聚簇。 如果命令中指定了索引名,则使用该索引隐含的顺序,并将该索引配置为聚簇所用的索引。 (这同样适用于传给 CLUSTER 命令的索引。) 如果未指定索引名,则使用已经被配置为聚簇索引的索引;如果没有这样的索引,就会报错。 可以使用 ALTER TABLE ... CLUSTER ON 手工设置索引,也可以使用 ALTER TABLE ... SET WITHOUT CLUSTER 清除设置。
如果在 REPACK USING INDEX 中没有指定表名,那么所有已经定义了聚簇索引、且调用用户有权限访问的表都会被处理。
聚簇是一次性操作:表随后被更新时,这些更改不会继续保持聚簇。也就是说,并不会尝试根据索引顺序存储新行或更新后的行。 (如果愿意,可以通过再次发出该命令来定期重新聚簇。 此外,把表的 fillfactor 存储参数设置为低于 100%,有助于在更新期间保持聚簇顺序,因为如果页面上有足够空间,更新后的行会保留在同一页面上。)
如果你是在表内随机访问单行,那么表中数据的实际顺序并不重要。 但如果你倾向于更频繁地访问某些数据,并且有一个能把它们分组在一起的索引,那么使用聚簇就会受益。 如果你从表中请求一个索引值范围,或者请求一个匹配多行的单个索引值,聚簇会有所帮助,因为一旦索引确定了第一个匹配行所在的表页,其他匹配行很可能已经在同一个表页上,因此可以节省磁盘访问并加快查询。
REPACK 可以使用指定索引上的索引扫描(如果该索引是 b-tree),也可以先顺序扫描再排序来重新排列表。 它会尝试根据规划器代价参数和可用统计信息,选择更快的方法。
由于规划器会记录有关表排序方式的统计信息,建议在新近重整过的表上运行 ANALYZE。 否则,规划器可能会做出糟糕的查询计划选择。
当使用索引扫描,或者使用不带排序的顺序扫描时,会创建一个临时副本,其中包含按索引顺序排列的表数据。 同时也会为表上的每个索引创建临时副本。因此,你需要至少等于表大小与索引大小之和的磁盘可用空间。
当使用顺序扫描加排序时,还会创建一个临时排序文件,因此峰值临时空间需求最多可达到表大小的两倍,再加上索引大小。 这种方法通常比索引扫描方法更快,但如果磁盘空间要求无法接受,可以通过临时将 enable_sort 设为 off 来禁用这种选择。
建议在重整之前,将 maintenance_work_mem 设置为一个相当大的值(但不要超过你可为 REPACK 操作预留的内存总量)。
table_name #表的名称(可以带模式限定)。
column_name #要分析的特定列名称。默认是所有列。如果指定了列列表,则还必须同时指定 ANALYZE。
index_name #索引名称。
CONCURRENTLY #允许其他事务在重整期间使用该表。
在内部,REPACK 会把表内容(忽略死元组)复制到一个新文件中,并按指定索引排序,同时还会为每个索引创建一个新文件。 然后它交换表及其所有索引的旧文件和新文件,并删除旧文件。需要 ACCESS EXCLUSIVE 锁,以确保处理过程中旧文件不会变化,因为这些变化会因交换而丢失。
使用 CONCURRENTLY 选项时,ACCESS EXCLUSIVE 锁只会在交换表文件和索引文件时获取。 在创建新表和索引文件期间发生的数据更改会通过逻辑解码(Chapter 47)捕获,并在请求 ACCESS EXCLUSIVE 锁之前应用。 因此,该锁通常只会持有完成文件交换所需的时间,应该相当短。 不过,如果 REPACK 等待锁期间对表做了太多数据更改,那么在持有 ACCESS EXCLUSIVE 锁时,必须先处理这些更改,然后才能交换文件,因此这段时间仍可能比较明显。
请注意,带 CONCURRENTLY 选项的 REPACK 不会尝试重新排列在重整开始之后插入到表中的行。 还要注意,REPACK 可能会因为其他事务在重整期间对该表执行的 DDL 命令而无法完成。
除了 Notes on Resources 中解释的临时空间要求之外,CONCURRENTLY 选项还可能会稍微增加一些临时空间的使用。 原因是,其他事务可以执行 DML 操作,而这些操作在 REPACK 复制完旧文件中的所有现有元组之前无法应用到新文件中。 因此,在复制期间插入到旧文件中的那些元组也会被单独存储在一个临时文件中,直到它们能够被处理为止。
CONCURRENTLY 选项不能在以下情况下使用:
该表是 UNLOGGED。
该表是分区表。
该表没有主键,也没有基于索引的副本身份。
该表是系统目录或 TOAST 表。
REPACK 在事务块中执行。
max_repack_replication_slots 配置参数不允许再创建一个额外的复制槽。
带 CONCURRENTLY 选项的 REPACK 不是 MVCC 安全的,详见 Section 13.6。
VERBOSE #以 INFO 级别打印每个表被重整时的进度报告。
ANALYZEANALYSE #在重整表之后对该表执行 ANALYZE。目前这只在指定单个(非分区)表时受支持。
boolean #指定所选选项应开启还是关闭。可以写 TRUE、ON 或 1 来启用该选项,也可以写 FALSE、OFF 或 0 来禁用它。boolean 值也可以省略,此时默认视为 TRUE。
要重整表,必须对该表具有 MAINTAIN 特权。
在 REPACK 运行期间,search_path 会临时改为 pg_catalog, pg_temp。
每个运行 REPACK 的后端都会在 pg_stat_progress_repack 视图中报告其进度。有关详细信息,请参见 Section 27.4.3。
重整分区表时,会重整其每个分区。如果指定了索引,则每个分区都会使用该索引的分区来重整。对分区表执行 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 语句。