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
提供了一种通过编写新版本的索引来减少索引空间消耗的方法,而不使用无效页面。有关更多信息,请参阅第 25.2 节。您已更改了索引的存储参数(例如 fillfactor),并希望确保更改已完全生效。
如果索引构建因
CONCURRENTLY
选项而失败,则该索引将保留为“invalid”。此类索引无用,但使用REINDEX
重建它们可能很方便。请注意,只有REINDEX INDEX
才能对无效索引执行并发构建。
参数
INDEX
重新创建指定的索引。当与分区索引一起使用时,此形式的
REINDEX
不能在事务块内执行。TABLE
重新创建指定表的所有索引。如果表具有辅助“TOAST”表,则也会重新对其编制索引。当与分区表一起使用时,此形式的
REINDEX
不能在事务块内执行。SCHEMA
重新创建指定架构的所有索引。如果此架构的表具有辅助“TOAST”表,则也会重新对其编制索引。还会处理共享系统目录上的索引。此形式的
REINDEX
不能在事务块内执行。DATABASE
重新创建当前数据库中除系统目录之外的所有索引。不会处理系统目录上的索引。此形式的
REINDEX
不能在事务块内执行。SYSTEM
重新创建当前数据库中系统目录上的所有索引。包括共享系统目录上的索引。不会处理用户表上的索引。此形式的
REINDEX
不能在事务块内执行。name
要重新编制索引的特定索引、表或数据库的名称。索引和表名称可以限定架构。目前,
REINDEX DATABASE
和REINDEX SYSTEM
只能重新编制当前数据库的索引。它们的参数是可选的,并且必须与当前数据库的名称匹配。CONCURRENTLY
使用此选项时,PostgreSQL 将重建索引,而不会采用任何锁定,以防止对表进行并发插入、更新或删除;而标准索引重建会锁定表上的写入(但不读取),直到完成。使用此选项时需要注意一些注意事项 - 参见下面的同时重建索引。
对于临时表,
REINDEX
始终是非并发的,因为没有其他会话可以访问它们,并且非并发重新索引更便宜。TABLESPACE
指定将在新表空间上重建索引。
VERBOSE
在重新索引每个索引时打印进度报告。
boolean
指定是否应启用或禁用所选选项。您可以编写
TRUE
、ON
或1
来启用该选项,而编写FALSE
、OFF
或0
来禁用它。boolean
值也可以省略,在这种情况下,假定为TRUE
。new_tablespace
将在其中重建索引的表空间。
注释
如果您怀疑用户表上的索引损坏,您可以使用REINDEX INDEX
或REINDEX TABLE
简单地重建该索引或表上的所有索引。
如果您需要从系统表上的索引损坏中恢复,情况会更加困难。在这种情况下,系统本身没有使用任何可疑索引非常重要。(实际上,在这种情况下,您可能会发现服务器进程在启动时立即崩溃,因为依赖于损坏的索引。)为了安全地恢复,必须使用-P
选项启动服务器,该选项阻止它使用索引进行系统目录查找。
执行此操作的一种方法是关闭服务器并使用命令行中包含-P
选项启动单用户PostgreSQL服务器。然后,可以根据您要重建的程度发出REINDEX DATABASE
、REINDEX SYSTEM
、REINDEX TABLE
或REINDEX INDEX
。如果您不确定,请使用REINDEX SYSTEM
选择重建数据库中的所有系统索引。然后退出单用户服务器会话并重新启动常规服务器。有关如何与单用户服务器界面进行交互的更多信息,请参见postgres参考页。
或者,可以在其命令行选项中包含-P
来启动常规服务器会话。执行此操作的方法因客户端而异,但在所有基于libpq的客户端中,都可以在启动客户端之前将PGOPTIONS
环境变量设置为-P
。请注意,虽然此方法不需要锁定其他客户端,但在修复完成之前,最好阻止其他用户连接到损坏的数据库。
REINDEX
类似于删除并重新创建索引,因为索引内容会从头开始重建。然而,锁定注意事项却大不相同。REINDEX
会锁定索引父表的写入,但不会锁定读取。它还会对正在处理的特定索引获取ACCESS EXCLUSIVE
锁,这会阻止尝试使用该索引的读取。特别是,查询计划程序会尝试对表的每个索引获取ACCESS SHARE
锁,而与查询无关,因此REINDEX
会阻止几乎所有查询,但某些已缓存且不使用此索引的已准备查询除外。相比之下,DROP INDEX
会暂时对父表获取ACCESS EXCLUSIVE
锁,从而阻止写入和读取。随后的CREATE INDEX
会锁定写入,但不会锁定读取;由于索引不存在,因此不会有读取尝试使用它,这意味着不会发生阻止,但读取可能会被迫进行昂贵的顺序扫描。
重新索引单个索引或表需要成为该索引或表的拥有者。重新索引架构或数据库需要成为该架构或数据库的拥有者。特别注意,非超级用户因此可以重建其他用户拥有的表的索引。但是,作为特殊例外,当REINDEX DATABASE
、REINDEX SCHEMA
或REINDEX SYSTEM
由非超级用户发出时,将跳过共享目录上的索引,除非用户拥有该目录(通常不会出现这种情况)。当然,超级用户始终可以重新索引任何内容。
使用REINDEX INDEX
或REINDEX TABLE
分别支持重新索引分区索引或分区表。指定的分区关系的每个分区都在单独的事务中重新索引。在处理分区表或索引时,这些命令不能在事务块内使用。
在对分区索引或表使用REINDEX
时使用TABLESPACE
子句时,只会更新叶分区表的表空间引用。由于分区索引不会更新,因此建议单独对它们使用ALTER TABLE ONLY
,以便任何附加的新分区继承新的表空间。如果失败,它可能无法将所有索引移动到新的表空间。重新运行该命令将重建所有叶分区并将以前未处理的索引移动到新的表空间。
如果SCHEMA
、DATABASE
或SYSTEM
与TABLESPACE
一起使用,系统关系将被跳过,并且会生成一个WARNING
。TOAST 表上的索引会重建,但不会移动到新的表空间。
并发重建索引
重建索引会干扰数据库的常规操作。通常情况下,PostgreSQL会锁定要重建索引的表,禁止写入,并通过一次扫描表来执行整个索引构建。其他事务仍可以读取表,但如果它们尝试在表中插入、更新或删除行,它们将阻塞,直到索引重建完成。如果系统是实时生产数据库,这可能会产生严重影响。非常大的表可能需要花费很多小时才能建立索引,即使对于较小的表,索引重建也会锁定写入者,对于生产系统来说,时间过长是不可接受的。
PostgreSQL支持以最少锁定写入的方式重建索引。通过指定REINDEX
的CONCURRENTLY
选项来调用此方法。当使用此选项时,PostgreSQL必须对每个需要重建的索引执行两次表扫描,并等待所有可能使用该索引的现有事务终止。此方法比标准索引重建需要更多的总工作量,并且由于需要等待可能修改索引的未完成事务,因此完成所需的时间明显更长。但是,由于它允许在重建索引时继续进行正常操作,因此此方法对于在生产环境中重建索引非常有用。当然,索引重建带来的额外 CPU、内存和 I/O 负载可能会减慢其他操作的速度。
并发重新索引中会发生以下步骤。每个步骤都在单独的事务中运行。如果有多个索引需要重建,则每个步骤在转到下一步之前都会循环遍历所有索引。
将新的临时索引定义添加到目录
pg_index
中。此定义将用于替换旧索引。在会话级别对正在重新索引的索引及其关联表获取SHARE UPDATE EXCLUSIVE
锁,以防止在处理过程中进行任何架构修改。为每个新索引执行一次构建索引的首次传递。一旦构建索引,其标志
pg_index.indisready
会切换到 “true” 以使其准备好插入,在执行构建的事务完成后对其他会话可见。此步骤对每个索引在单独的事务中完成。然后执行第二次传递以添加在首次传递运行时添加的元组。此步骤也对每个索引在单独的事务中完成。
所有引用索引的约束都更改为引用新索引定义,并且索引的名称也会更改。此时,
pg_index.indisvalid
会切换到 “true” 以表示新索引,切换到 “false” 以表示旧索引,并且会执行缓存失效,导致引用旧索引的所有会话失效。在等待可能引用旧索引的正在运行的查询完成之后,旧索引的
pg_index.indisready
会切换到 “false” 以防止任何新的元组插入。删除旧索引。释放索引和表的
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 TABLE
或REINDEX INDEX
命令,但不能执行REINDEX CONCURRENTLY
。
与任何长时间运行的事务一样,表上的REINDEX
可能会影响其他任何表上的并发VACUUM
可以删除哪些元组。
REINDEX SYSTEM
不支持CONCURRENTLY
,因为系统目录无法并发重新索引。
此外,排除约束的索引无法并发重新索引。如果在此命令中直接命名了此类索引,则会引发错误。如果并发重新索引具有排除约束索引的表或数据库,则将跳过这些索引。(可以在没有CONCURRENTLY
选项的情况下重新索引此类索引。)
每个运行REINDEX
的后端将在pg_stat_progress_create_index
视图中报告其进度。有关详细信息,请参见第 28.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
命令。