支援的版本:目前版本 (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

62.5. 索引唯一性檢查 #

PostgreSQL 使用唯一索引來強制執行 SQL 唯一性約束,這種索引不允許有多個具有相同鍵值的條目。 支援此功能的存取方法會將 amcanunique 設定為 true。(目前只有 b-tree 支援它。) 在 INCLUDE 子句中列出的欄位在強制執行唯一性時不會被考慮。

由於 MVCC 的緣故,總是必須允許重複的條目在索引中實際存在:這些條目可能引用單一邏輯列的連續版本。 我們實際要強制執行的行為是,沒有任何 MVCC 快照可以包含兩個具有相等索引鍵值的列。 這可分解為以下情況,在將新列插入唯一索引時必須進行檢查:

  • 如果發生衝突的有效列已被目前交易刪除,則沒有問題。(特別是,由於 UPDATE 總是在插入新版本之前刪除舊列版本,這將允許在不更改鍵值的情況下更新列。)

  • 如果衝突的列是由尚未提交的交易插入的,則潛在的插入者必須等待,以查看該交易是否提交。 如果它回滾,則沒有衝突。 如果它在沒有再次刪除衝突列的情況下提交,則會發生唯一性違規。(實際上,我們只是等待其他交易結束,然後完全重新執行可見性檢查。)

  • 同樣地,如果衝突的有效列已由尚未提交的交易刪除,則潛在的插入者必須等待該交易提交或中止,然後重複測試。

此外,在根據上述規則報告唯一性違規之前,存取方法必須重新檢查要插入的列的即時性。 如果它已提交死亡,則不應報告任何違規。(這種情況不會發生在插入剛由目前交易建立的列的普通情況下。 但是,它可能在 CREATE UNIQUE INDEX CONCURRENTLY 期間發生。)

我們要求索引存取方法自行應用這些測試,這意味著它必須深入堆疊以檢查顯示具有重複鍵值的任何列的提交狀態(根據索引內容)。 毫無疑問,這很醜陋且非模組化,但它可以節省多餘的工作:如果我們進行單獨的探測,則在尋找插入新列索引條目的位置時,對於衝突列的索引查找基本上會重複進行。 此外,除非衝突檢查是插入新索引條目的整體部分,否則沒有明顯的方法可以避免競爭條件。

如果唯一性約束是可以延遲的,則會增加複雜性:我們需要能夠為新列插入索引條目,但將任何唯一性違規錯誤延遲到語句結尾甚至更晚。 為了避免不必要的索引重複搜尋,索引存取方法應在初始插入期間進行初步的唯一性檢查。 如果這表明絕對沒有衝突的即時元組,則完成。 否則,我們排程在強制執行約束時進行重新檢查。 如果在重新檢查時,插入的元組和另一個具有相同鍵值的元組都是即時的,則必須報告該錯誤。(請注意,為此,即時 實際上表示 索引條目的 HOT 鏈中的任何元組都是即時的。) 為了實作此功能,aminsert 函數會傳遞一個 checkUnique 參數,該參數具有以下其中一個值:

  • UNIQUE_CHECK_NO 表示不應進行任何唯一性檢查(這不是唯一索引)。

  • UNIQUE_CHECK_YES 表示這是一個不可延遲的唯一索引,並且必須立即進行唯一性檢查,如上所述。

  • UNIQUE_CHECK_PARTIAL 表示唯一性約束是可以延遲的。 PostgreSQL 將使用此模式來插入每個列的索引條目。 存取方法必須允許重複的條目進入索引,並且透過從 aminsert 傳回 false 來報告任何潛在的重複項。 對於傳回 false 的每個列,將排程延遲的重新檢查。

    存取方法必須識別任何可能違反唯一性約束的資料列,但回報誤判並非錯誤。 這允許在不必等待其他交易完成的情況下執行檢查;此處回報的衝突不被視為錯誤,並將稍後重新檢查,屆時它們可能不再是衝突。

  • UNIQUE_CHECK_EXISTING 表示這是對已被回報為潛在唯一性違規的資料列的延遲重新檢查。 雖然這是透過呼叫 aminsert 實作的,但存取方法在這種情況下不得插入新的索引條目。 索引條目已經存在。相反地,存取方法必須檢查是否還有另一個活動的索引條目。 如果是,且目標資料列仍然處於活動狀態,則回報錯誤。

    建議在 UNIQUE_CHECK_EXISTING 呼叫中,存取方法進一步驗證目標資料列實際上在索引中確實存在一個條目,如果沒有,則回報錯誤。 這是一個好主意,因為傳遞給 aminsert 的索引元組值將被重新計算。 如果索引定義涉及實際上不是真正不可變的函數,我們可能正在檢查索引的錯誤區域。 檢查在重新檢查中是否找到目標資料列,可以驗證我們正在掃描與原始插入中使用的相同元組值。

提交更正

如果您在文件中看到任何不正確、與您使用特定功能的經驗不符或需要進一步澄清的地方,請使用此表單來回報文件問題。