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

63.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 的索引元組值將被重新計算。如果索引定義涉及非真正不可變(immutable)的函式,我們可能會檢查索引的錯誤區域。檢查目標行在重新檢查中是否被找到,可以驗證我們是否正在掃描與原始插入中使用的元組值相同的元組值。

提交更正

如果您在文件中發現任何不正確、與您對特定功能的體驗不符或需要進一步澄清的內容,請使用 此表格 報告文件問題。