2024年9月26日: PostgreSQL 17 发布!
支持的版本:当前 (17) / 16 / 15 / 14 / 13 / 12
开发版本:devel
不支持的版本: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 支持重新索引分区索引或分区表。 指定的分区关系的每个分区都在单独的事务中重新索引。 当在分区表或索引上工作时,这些命令不能在事务块内使用。

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

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

并发重建索引

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

PostgreSQL 支持以最小的写锁定来重建索引。此方法通过在 REINDEX 命令中指定 CONCURRENTLY 选项来调用。使用此选项时,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 命令将失败,但除了预先存在的索引之外,还会留下一个 invalid 的新索引。此索引将被忽略,不会用于查询目的,因为它可能不完整;但是它仍然会消耗更新开销。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 命令重新索引此类索引。)

每个运行 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 命令。

提交更正

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