REINDEX — 重建索引
REINDEX [ (option[, ...] ) ] { INDEX | TABLE | SCHEMA } [ CONCURRENTLY ]nameREINDEX [ (option[, ...] ) ] { DATABASE | SYSTEM } [ CONCURRENTLY ] [name] 其中option可以是以下之一: CONCURRENTLY [boolean] TABLESPACEnew_tablespaceVERBOSE [boolean]
REINDEX使用索引所属表中存储的数据重建索引, 并替换索引的旧副本。以下几种场景适合使用REINDEX:
一个索引已经损坏,不再包含有效数据。尽管理论上这不该发生, 但在实践中索引可能因软件缺陷或硬件故障而损坏。 REINDEX提供了一种恢复方法。
一个索引已经“膨胀”,也就是说其中包含许多空页或几乎为空 的页。在 PostgreSQL 中,B-树索引在某些不常见 的访问模式下可能出现这种情况。REINDEX可通过写入一个 不含死页的新版本索引来减少索引的空间消耗。详见Section 24.2。
你修改了某个索引的存储参数(例如 fillfactor),并希望确保该更改已经 完全生效。
如果使用CONCURRENTLY选项构建索引失败,该索引会被 保留为“无效”。这类索引没有用处,但用REINDEX 重建它们可能很方便。注意,只有REINDEX INDEX才能在 无效索引上执行并发构建。
INDEX #重新创建指定的索引。如果用于分区索引,则这种形式的 REINDEX不能在事务块内执行。
TABLE #重新创建指定表的所有索引。如果该表有一个辅助“TOAST”表, 也会对其重新索引。如果用于分区表,则这种形式的 REINDEX不能在事务块内执行。
SCHEMA #重新创建指定模式中的所有索引。如果该模式中的某个表有一个辅助 “TOAST”表,也会对其重新索引。共享系统目录上的索引也会被 处理。这种形式的REINDEX不能在事务块内执行。
DATABASE #重新创建当前数据库中除系统目录外的所有索引。 系统目录上的索引不会被处理。这种形式的REINDEX不能 在事务块内执行。
SYSTEM #重新创建当前数据库内系统目录上的所有索引。共享系统目录上的索引也包 含在内。用户表上的索引不会被处理。这种形式的 REINDEX不能在事务块内执行。
name #要重新索引的特定索引、表或数据库的名称。索引名和表名可以带模式限 定。目前,REINDEX DATABASE和 REINDEX SYSTEM只能对当前数据库重新索引。它们的参 数是可选的,但如果给出,就必须与当前数据库名匹配。
CONCURRENTLY #使用此选项时,PostgreSQL会在不获取任何会 阻止表上并发插入、更新或删除的锁的情况下重建索引;而标准索引重建会 阻止表上的写入(但不阻止读取),直到完成。使用此选项时有若干注意事 项,见下文Rebuilding Indexes Concurrently。
对于临时表,REINDEX始终以非并发方式执行,因为没 有其他会话可以访问它们,而且非并发重新索引开销更小。
TABLESPACE #指定索引将在新的表空间中重建。
VERBOSE #在每个索引被重建时,以 INFO 级别打印进度报告。
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运行期间,search_path会临时改为pg_catalog, pg_temp。
对单个索引或表重新索引,需要在相应表上具有 MAINTAIN 权限。注意,对分区索引或分区表执行REINDEX时,需 要在该分区表上具有 MAINTAIN 权限,但在处理各个分 区时会跳过权限检查。对模式或数据库重新索引,则需要是该模式或数据库的 拥有者,或者具有pg_maintain角色的 权限。特别要注意,这意味着非超级用户也可能重建其他用户拥有的表的索引。 不过,作为一个特殊例外,REINDEX DATABASE、 REINDEX SCHEMA和REINDEX SYSTEM 会跳过共享系统目录上的索引,除非用户对该目录具有 MAINTAIN权限。
分别可使用REINDEX INDEX和 REINDEX TABLE对分区索引和分区表重新索引。指定分 区关系的每个分区都会在单独的事务中重新索引。在处理分区表或分区索引 时,这些命令不能在事务块内使用。
当对分区索引或分区表执行带TABLESPACE子句的 REINDEX时,只有叶分区的表空间引用会被更新。由于 分区索引本身不会更新,建议另外对相应的分区表单独执行 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命令会失败,但除了原有索引外,还会留下一个 “无效”的新索引。这个索引在查询时会被忽略,因为它可能不 完整;不过它仍会带来更新开销。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 TABLE或REINDEX INDEX 命令可以在事务块内执行,而REINDEX CONCURRENTLY不 行。
与任何长时间运行的事务一样,对表执行REINDEX可能 会影响并发VACUUM在其他表上能够移除哪些元组。
REINDEX SYSTEM不支持 CONCURRENTLY,因为系统目录不能并发重新索引。
此外,用于排他约束的索引不能并发重新索引。如果在此命令中直接指定这样 的索引,就会报错。如果并发地对包含排他约束索引的表或数据库重新索引, 这些索引将被跳过。(不使用CONCURRENTLY选项时,可 以重新索引这类索引。)
每个运行REINDEX的后端都会在 pg_stat_progress_create_index视图中报告其进 度。详见Section 27.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命令。
如果您发现文档中有不正确的内容、与您使用特定功能的经验不符或需要进一步说明,请使用此表单来报告文档问题。