9.3 9.4 9.5 9.6 10 11 12 13 14 Current(15)
阿里云PostgreSQL 问题报告 纠错本页面

REINDEX

REINDEX — 重建索引

大纲

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

其中 选项 可以是以下之一:

    CONCURRENTLY [ boolean ]
    TABLESPACE new_tablespace
    VERBOSE [ boolean ]

描述

REINDEX使用索引的表里存储的数据重建一个索引, 并且替换该索引的旧拷贝。有一些场景需要使用REINDEX

  • 一个索引已经损坏,并且不再包含合法数据。尽管理论上这不会发生, 实际上索引会因为软件缺陷或硬件失效损坏。 REINDEX提供了一种恢复方法。

  • 一个索引变得臃肿,其中包含很多空的或者近乎为空的页面。 PostgreSQL中的 B-树索引在特定的非 常规访问模式下可能会发生这种情况。REINDEX 提供了一种方法来减少索引的空间消耗,即制造一个新版本的索引,其中没有 死亡页面。详见第 25.2 节

  • 修改了一个索引的存储参数(例如填充因子),并且希望确保这种修改完全 生效。

  • 如果索引在用CONCURRENTLY选项创建失败,该索引保留为一个invalid。 这类索引是无用的,但是可以方便的用REINDEX来重建它们。注意,只有REINDEX INDEX可以在无效的索引上执行并发创建。

参数

INDEX

重新创建指定的索引。 当与分区索引一起使用时,REINDEX的这种形式不能在事务块内部执行。

TABLE

重新创建指定表的所有索引。 如果该表有一个二级TOAST表,它也会被重新索引。 当与分区表一起使用时,REINDEX的这种形式不能再事务块内部执行。

SCHEMA

重建指定方案的所有索引。如果这个方案中的一个表有次级的TOAST表,它也会被重建索引。共享系统目录上的索引也会被处理。这种形式的REINDEX不能在事务块内执行。

DATABASE

重新创建当前数据库内的所有索引。共享的系统目录上的索引也会被 处理。这种形式的REINDEX不能在一个 事务块内执行。

SYSTEM

重新创建当前数据库中在系统目录上的所有索引。共享系统目录上的 索引也被包括在内。用户表上的索引则不会被处理。这种形式的 REINDEX不能在一个事务块内执行。

name

要被重新索引的特定索引、表或者数据库的名字。索引和表名可以被 模式限定。当前,REINDEX DATABASEREINDEX SYSTEM只能重新索引当前数据库,因此 它们的参数必须匹配当前数据库的名称。

CONCURRENTLY

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

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

TABLESPACE

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

VERBOSE

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

boolean

指定选择的选项是否打开或关闭。 你可以写TRUEON、或者1 以激活该选项,以及FALSEOFF、或0以禁用它。 boolean值也可以被省略,在假设为TRUE的情况下。

new_tablespace

将被重建索引的表空间。

注解

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

如果你需要从一个系统表上的索引损坏中恢复,就更困难一些。在 这种情况下,对系统来说重要的是没有使用过任何可疑的索引本身( 实际上,这种场景中,你可能会发现服务器进程会在启动时立刻崩溃, 这是因为对于损坏的索引的依赖)。要安全地恢复,服务器必须用 -P选项启动,这将阻止它使用索引来进行系统 目录查找。

这样做的一种方法是关闭服务器,并且启动一个单用户的 PostgreSQL服务器,在其命令行 中包括-P选项。然后,可以发出 REINDEX DATABASEREINDEX SYSTEMREINDEX TABLE或者REINDEX INDEX, 具体使用哪个命令取决于你想要重构多少东西。如果有疑问,可以使用 REINDEX SYSTEM来选择重建数据库中的所有系统索引。 然后退出单用户服务器会话并且重启常规的服务器。更多关于如何与 单用户服务器接口交互的内容请见postgres参考页。

在另一种方法中,可以开始一个常规的服务器会话,在其命令行选项 中包括-P。这样做的方法与客户端有关,但是在 所有基于libpq的客户端中都可以在开始客户端 之前设置PGOPTIONS环境变量为-P。 注意虽然这种方法不要求用锁排斥其他客户端,在修复完成之前避免 其他用户连接到受损的数据库才是更加明智的。

REINDEX类似于索引的删除和重建,因为索引内容是从头开始重建的。 但是,锁定考虑是非常不同的。REINDEX锁定了索引的父表的写入,但不锁定读取。 它还对正在处理的特定索引采取了ACCESS EXCLUSIVE锁定,这将阻止尝试使用该索引的读取。 特别是,查询规划器尝试对表的每个索引采取ACCESS SHARE锁定,而不考虑查询, 因此REINDEX几乎阻止除了一些已缓存计划且不使用这个索引的准备查询之外的任何查询。 相比之下,DROP INDEX瞬间对父表采取ACCESS EXCLUSIVE锁定, 阻止写入和读取。随后的CREATE INDEX锁定了写入但不锁定读取; 因为索引不存在,没有读取会尝试使用它,这意味着不会有阻塞,但读取可能被迫进入昂贵的顺序扫描。

重新索引单独一个索引或者表要求用户是该索引或表的拥有者。对方案或数据库重建索引要求是该方案或者数据库的拥有者。请特别注意,因此非超级用户有时无法重建其他用户拥有的表上的索引。不过,作为一种特例,当一个非超级用户发出REINDEX DATABASEREINDEX SCHEMA或者REINDEX SYSTEM时,共享目录上的索引将被跳过,除非该用户拥有该目录(通常不会是这样)。当然,超级用户总是可以重建所有的索引。

可以支持对分区索引或分区表重建索引,分别通过REINDEX INDEXREINDEX TABLE。 指定分区关系的每个分区在一个单独的事务中重建索引。 当工作在分区表或索引上面时,那些命令不能在事务块内部使用。

当在已分区的索引或表上使用TABLESPACE子句和REINDEX时,只有叶子分区的表空间引用被更新。 由于分区索引不会被更新,因此建议单独对它们使用ALTER TABLE ONLY,以便任何附加的新分区继承新的表空间。 失败的话,它可能没有将所有的索引转移到新的表空间。 重新运行该命令将重新构建所有叶子分区,并将以前未处理的索引转移到新的表空间。

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

Rebuilding Indexes Concurrently

重建索引可能会影响数据库的常规操作。通常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命令将失败,但会留下一个 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将在pg_stat_progress_create_index视图中报告它的进度。 详见第 28.4.2 节

示例

重建单个索引:

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命令。

See Also

CREATE INDEX, DROP INDEX, reindexdb, 第 28.4.2 节