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

提交更正

如果您在文件中看到任何不正確、與您的實際使用經驗不符或需要進一步說明的內容,請使用此表單報告文件問題。