支持的版本:当前17)/ 16 / 15 / 14 / 13
开发版本:开发版
不支持的版本:12 / 11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1 / 9.0 / 8.4 / 8.3 / 8.2 / 8.1 / 8.0 / 7.4 / 7.3 / 7.2 / 7.1

REINDEX

REINDEX — 重建索引

概要

REINDEX [ ( option [, ...] ) ] { INDEX | TABLE | SCHEMA } [ CONCURRENTLY ] name
REINDEX [ ( option [, ...] ) ] { DATABASE | SYSTEM } [ CONCURRENTLY ] [ name ]

where option can be one of:

    CONCURRENTLY [ boolean ]
    TABLESPACE new_tablespace
    VERBOSE [ boolean ]

描述

REINDEX 使用索引表中的数据重建索引,替换旧的索引副本。在以下几种情况下可以使用 REINDEX

  • 索引已损坏,不再包含有效数据。虽然理论上不应该发生这种情况,但在实践中,由于软件错误或硬件故障,索引可能会损坏。REINDEX 提供了一种恢复方法。

  • 索引变得“膨胀”,即它包含许多空或几乎为空的页面。这可能会在 PostgreSQL 中使用 B 树索引在某些不常见的访问模式下发生。REINDEX 提供了一种通过编写一个不包含死页面的新版本索引来减少索引空间消耗的方法。有关详细信息,请参阅 第 24.2 节

  • 您已更改索引的存储参数(例如 fillfactor),并希望确保更改已完全生效。

  • 如果索引构建失败并带有 CONCURRENTLY 选项,则此索引将保留为“无效”。此类索引无用,但可以使用 REINDEX 重建它们很方便。请注意,只有 REINDEX INDEX 才能在无效索引上执行并发构建。

参数

INDEX

重新创建指定的索引。当与分区索引一起使用时,此形式的 REINDEX 无法在事务块内执行。

TABLE

重新创建指定表的所有索引。如果该表有一个辅助的“TOAST”表,也会被重新索引。当与分区表一起使用时,此形式的 REINDEX 无法在事务块内执行。

SCHEMA

重新创建指定模式的所有索引。如果此模式的表有一个辅助的“TOAST”表,也会被重新索引。共享系统目录上的索引也会被处理。此形式的 REINDEX 无法在事务块内执行。

DATABASE

重新创建当前数据库中的所有索引,除了系统目录。系统目录上的索引不会被处理。此形式的 REINDEX 无法在事务块内执行。

SYSTEM

重新创建当前数据库中系统目录上的所有索引。包括共享系统目录上的索引。不会处理用户表上的索引。此形式的 REINDEX 无法在事务块内执行。

name

要重新索引的特定索引、表或数据库的名称。索引和表名可以带有模式限定。目前,REINDEX DATABASEREINDEX SYSTEM 只能重新索引当前数据库。它们的参数是可选的,并且必须与当前数据库的名称匹配。

CONCURRENTLY

使用此选项时,PostgreSQL 将重建索引,而不会获取任何阻止并发插入、更新或删除表的锁;而标准的索引重建会锁定表上的写入(但不会锁定读取),直到完成。使用此选项时需要注意几个警告 — 请参阅下面的 并发重建索引

对于临时表,REINDEX 始终是非并发的,因为没有其他会话可以访问它们,并且非并发重新索引成本更低。

TABLESPACE

指定将在新的表空间上重建索引。

VERBOSE

在重新索引每个索引时打印进度报告。

boolean

指定是否应打开或关闭所选选项。您可以编写 TRUEON1 来启用该选项,以及 FALSEOFF0 来禁用该选项。也可以省略 boolean 值,在这种情况下,将假定为 TRUE

new_tablespace

将在其中重建索引的表空间。

注意

如果您怀疑用户表上的索引损坏,可以使用 REINDEX INDEXREINDEX TABLE 简单地重建该索引或表上的所有索引。

如果您需要从系统表上的索引损坏中恢复,情况会更加困难。在这种情况下,重要的是系统本身不使用任何可疑的索引。(实际上,在这种情况下,您可能会发现由于依赖损坏的索引,服务器进程在启动时会立即崩溃。)为了安全恢复,必须使用 -P 选项启动服务器,这可以防止它使用索引进行系统目录查找。

一种方法是关闭服务器,并使用其命令行中包含的 -P 选项启动单用户 PostgreSQL 服务器。然后,可以根据要重建的程度发出 REINDEX DATABASEREINDEX SYSTEMREINDEX TABLEREINDEX INDEX。如有疑问,请使用 REINDEX SYSTEM 来选择重建数据库中的所有系统索引。然后退出单用户服务器会话并重新启动常规服务器。有关如何与单用户服务器界面交互的更多信息,请参阅 postgres 参考页。

或者,可以使用命令行选项中包含的 -P 启动常规服务器会话。执行此操作的方法因客户端而异,但在所有基于 libpq 的客户端中,可以在启动客户端之前将 PGOPTIONS 环境变量设置为 -P。请注意,虽然此方法不需要锁定其他客户端,但最好仍然阻止其他用户在修复完成之前连接到损坏的数据库。

REINDEX 类似于删除并重新创建索引,因为索引内容是从头开始重建的。但是,锁定注意事项有所不同。REINDEX 会锁定索引父表的写入,但不会锁定读取。它还在正在处理的特定索引上获取 ACCESS EXCLUSIVE 锁,这将阻止尝试使用该索引的读取。特别是,查询规划器会尝试在表的每个索引上获取 ACCESS SHARE 锁,而不管查询是什么,因此 REINDEX 会阻止几乎所有查询,除了某些已缓存其计划且不使用此索引的准备好的查询。相反,DROP INDEX 会在父表上临时获取 ACCESS EXCLUSIVE 锁,阻止写入和读取。随后的 CREATE INDEX 会锁定写入但不会锁定读取;由于索引不存在,因此没有读取会尝试使用它,这意味着不会有阻塞,但读取可能会被迫进行昂贵的顺序扫描。

REINDEX 运行时,search_path 会临时更改为 pg_catalog, pg_temp

对单个索引或表进行重新索引需要对该表拥有 MAINTAIN 权限。请注意,虽然对分区索引或表执行 REINDEX 命令需要对分区表拥有 MAINTAIN 权限,但此类命令在处理各个分区时会跳过权限检查。重新索引模式或数据库需要是该模式或数据库的所有者,或者拥有 pg_maintain 角色的权限。请特别注意,因此非超级用户也可以重建其他用户拥有的表的索引。但是,作为特殊例外,REINDEX DATABASEREINDEX SCHEMAREINDEX SYSTEM 将跳过共享目录上的索引,除非用户对该目录拥有 MAINTAIN 权限。

支持分别使用 REINDEX INDEXREINDEX TABLE 重新索引分区索引或分区表。指定的已分区关系的每个分区都在单独的事务中重新索引。在处理分区表或索引时,这些命令不能在事务块内使用。

TABLESPACE 子句与分区索引或表上的 REINDEX 一起使用时,仅更新叶子分区的表空间引用。由于不会更新分区索引,因此建议对它们单独使用 ALTER TABLE ONLY,以便附加的任何新分区都继承新的表空间。如果失败,可能并未将所有索引移动到新的表空间。重新运行该命令将重建所有叶子分区,并将以前未处理的索引移动到新的表空间。

如果将 SCHEMADATABASESYSTEMTABLESPACE 一起使用,则将跳过系统关系,并且将生成单个 WARNING。TOAST 表上的索引将被重建,但不会移动到新的表空间。

并发重建索引

重建索引可能会干扰数据库的常规操作。通常,PostgreSQL 会锁定其索引被重建的表以防止写入,并使用对表的单次扫描执行整个索引构建。其他事务仍然可以读取该表,但如果它们尝试在该表中插入、更新或删除行,则它们将阻塞,直到索引重建完成。如果系统是活动的生产数据库,这可能会产生严重的影响。非常大的表可能需要几个小时才能建立索引,即使对于较小的表,索引重建也可能会将写入器锁定一段时间,而这对生产系统来说是不可接受的。

PostgreSQL 支持以最小的写入锁定来重建索引。通过指定 REINDEXCONCURRENTLY 选项来调用此方法。使用此选项时,PostgreSQL 必须对每个需要重建的索引执行两次表扫描,并等待可能使用该索引的所有现有事务终止。此方法需要比标准索引重建更多的工作总量,并且完成时间明显更长,因为它需要等待可能修改索引的未完成事务。但是,由于它允许在重建索引时继续进行正常操作,因此此方法对于在生产环境中重建索引很有用。当然,索引重建带来的额外的 CPU、内存和 I/O 负载可能会减慢其他操作。

并发重新索引会发生以下步骤。每个步骤都在单独的事务中运行。如果要重建多个索引,则每个步骤都会循环遍历所有索引,然后再移至下一步。

  1. 一个新的瞬态索引定义被添加到目录 pg_index 中。此定义将用于替换旧索引。在会话级别对正在重新索引的索引及其关联的表执行 SHARE UPDATE EXCLUSIVE 锁,以防止在处理期间进行任何模式修改。

  2. 为每个新索引完成构建索引的第一遍。构建索引后,其标志 pg_index.indisready 将切换为 true 以使其可以用于插入,从而使其在执行构建的事务完成后对其他会话可见。此步骤对每个索引在单独的事务中完成。

  3. 然后执行第二遍以添加在第一遍运行时添加的元组。此步骤也对每个索引在单独的事务中完成。

  4. 引用索引的所有约束都将更改为引用新索引定义,并且索引的名称将更改。此时,新索引的 pg_index.indisvalid 将切换为 true,旧索引的 pg_index.indisvalid 将切换为 false,并且完成缓存失效会导致引用旧索引的所有会话失效。

  5. 旧索引的 pg_index.indisready 将切换为 false,以防止任何新的元组插入,在等待可能引用旧索引的正在运行的查询完成后。

  6. 旧索引被删除。将释放索引和表的 SHARE UPDATE EXCLUSIVE 会话锁。

如果在重建索引时出现问题,例如唯一索引中存在唯一性冲突,则 REINDEX 命令将失败,但会在现有索引之外留下一个 无效 的新索引。此索引将被忽略以进行查询,因为它可能不完整;但是它仍会消耗更新开销。psql \d 命令会将此类索引报告为 INVALID

postgres=# \d tab
       Table "public.tab"
 Column |  Type   | Modifiers
--------+---------+-----------
 col    | integer |
Indexes:
    "idx" btree (col)
    "idx_ccnew" btree (col) INVALID

如果标记为 INVALID 的索引带有后缀 ccnew,则它对应于并发操作期间创建的瞬态索引,建议的恢复方法是使用 DROP INDEX 将其删除,然后再次尝试 REINDEX CONCURRENTLY。如果无效索引改为带有后缀 ccold,则它对应于无法删除的原始索引;建议的恢复方法是直接删除该索引,因为重建本身已成功。

常规索引构建允许在同一表上同时发生其他常规索引构建,但一次只能在表上发生一个并发索引构建。在这两种情况下,都不允许同时对表进行其他类型的模式修改。另一个区别是,可以在事务块内执行常规的 REINDEX TABLEREINDEX INDEX 命令,但不能执行 REINDEX CONCURRENTLY

像任何长时间运行的事务一样,表上的 REINDEX 可能会影响哪些元组可以通过任何其他表上的并发 VACUUM 删除。

REINDEX SYSTEM 不支持 CONCURRENTLY,因为系统目录不能并发重新索引。

此外,排除约束的索引不能并发重新索引。如果在此命令中直接命名此类索引,则会引发错误。如果并发重新索引具有排除约束索引的表或数据库,则将跳过这些索引。(可以在不使用 CONCURRENTLY 选项的情况下重新索引此类索引。)

每个运行 REINDEX 的后端都将在 pg_stat_progress_create_index 视图中报告其进度。有关详细信息,请参见 第 27.4.4 节

示例

重建单个索引

REINDEX INDEX my_index;

重建表 my_table 上的所有索引

REINDEX TABLE my_table;

重建特定数据库中的所有索引,而不信任系统索引已经是有效的

$ export PGOPTIONS="-P"
$ psql broken_db
...
broken_db=> REINDEX DATABASE broken_db;
broken_db=> \q

重建表的索引,而不会在重新索引过程中阻塞相关关系上的读取和写入操作

REINDEX TABLE CONCURRENTLY my_broken_table;

兼容性

SQL 标准中没有 REINDEX 命令。

提交更正

如果您在文档中看到任何不正确的内容、与您使用特定功能的体验不符的内容或需要进一步澄清的内容,请使用 此表单来报告文档问题。