2025年9月25日: PostgreSQL 18 发布!
支持的版本:当前18)/ 17 / 16 / 15 / 14 / 13
开发版本:devel
不支持的版本: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-tree 索引在某些不常见的访问模式下。 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

在重新索引每个索引时,在 INFO 级别打印进度报告。

boolean

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

new_tablespace

索引将被重建的新表空间。

注释

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

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

一种方法是关闭服务器并启动一个单用户 PostgreSQL 服务器,并在其命令行中包含 -P 选项。然后,可以根据您想要重建的范围发出 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,旧索引的切换到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,则它对应于无法删除的原始索引;建议的恢复方法是直接删除该索引,因为重建本身已成功。非零数字可能附加到无效索引名称的后缀以保持其唯一性,例如 _ccnew1_ccold2 等。

常规索引构建允许对同一表上的其他常规索引构建同时进行,但一次只能对一个表进行并发索引构建。在这两种情况下,在此期间不允许对表进行其他类型的模式修改。另一个不同之处在于,常规的 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 命令。

提交更正

如果您在文档中看到任何不正确、与您的实际使用经验不符或需要进一步说明的内容,请使用此表单报告文档问题。