PostgreSQL 索引唯一性檢查

2021-09-15 11:05 更新

PostgreSQL使用唯一索引來強(qiáng)制 SQL 唯一性約束,唯一 索引實(shí)際上是不允許多個(gè)項(xiàng)有相同鍵的索引。一個(gè)支持這個(gè)特性的訪問方法要 設(shè)置amcanunique為真(目前,只有 b-tree 支持它)。在強(qiáng)制唯一性時(shí)不會(huì)考慮INCLUDE子句中列出的列。

因?yàn)?MVCC,必須允許重復(fù)的項(xiàng)在物理上存在于索引之中:這些項(xiàng)可能指向某個(gè)單一邏輯行的后繼版本。實(shí)際想強(qiáng)制的行為是,任何 MVCC 快照都不能包含兩個(gè)具有相同索引鍵的行。在向一個(gè)唯一索引中插入一個(gè)新行時(shí)需要被檢查的情況可分解成:

  • 如果一個(gè)有沖突的合法行已被當(dāng)前事務(wù)刪除,這是可以的(特別是因?yàn)橐粋€(gè) UPDATE 總是在插入新版本之前刪除舊版本,這樣就允許一個(gè)行上的UPDATE 不改變鍵)。

  • 如果一個(gè)有沖突的行已經(jīng)被還未提交的事務(wù)插入,那么準(zhǔn)備插入的事務(wù)必須等待看看前面那個(gè)事務(wù)是否提交。如果它回滾額不會(huì)有沖突。如果它提交并且沒有刪除存在沖突的行,則有一個(gè)唯一性違背(實(shí)際上我們只是等待那個(gè)其他事務(wù)結(jié)束,然后在全部事務(wù)里重做可見性檢查)。

  • 類似的,如果一個(gè)有沖突的有效行被一個(gè)準(zhǔn)備提交的事務(wù)刪除,那么另外一個(gè)準(zhǔn)備插入的事務(wù)必須等待該事務(wù)提交或者退出,然后重做測(cè)試。

此外,在根據(jù)上述規(guī)則報(bào)告唯一性違背之前,訪問方法必須重新檢查剛被插入的行的存活性。如果已經(jīng)因?yàn)槭聞?wù)的提交而死亡,那么不應(yīng)當(dāng)報(bào)告任何違背(這種情況不可能出現(xiàn)在插入在同一事務(wù)中創(chuàng)建的行的普通場(chǎng)景中。但是在CREATE UNIQUE INDEX CONCURRENTLY的過程中是可能發(fā)生的)。

要求索引訪問方法自己應(yīng)用這些測(cè)試,這就意味著它必須到達(dá)堆來查看那些根據(jù) 索引內(nèi)容有重復(fù)鍵的任意行的提交狀態(tài)。這無(wú)疑是丑陋并且非模塊化的,但是這樣可以節(jié)約重復(fù)的工作:如果我們進(jìn)行一次獨(dú)立的探測(cè),那么查找一個(gè)沖突行的索引查找本質(zhì)上將在查找插入新行索引項(xiàng)位置時(shí)被重復(fù)。此外,沒有很明顯的方法來避免競(jìng)爭(zhēng)情況,除非沖突檢查是插入新索引項(xiàng)動(dòng)作的一部分。

如果唯一約束是可延遲的,就存在額外的復(fù)雜性:我們需要能夠?yàn)橐粋€(gè)新行插入一個(gè)索引項(xiàng),但是推遲任何唯一性違背錯(cuò)誤直到語(yǔ)句結(jié)束或者更晚。為了避免對(duì)索引不必要的重復(fù)搜索,索引訪問方法應(yīng)該在初始插入過程中做一次初步的唯一性檢查。如果顯示絕對(duì)不會(huì)有沖突的活元組,就可以完成。否則,我們計(jì)劃一次重新檢查,它將在強(qiáng)制約束的時(shí)候發(fā)生。在重新檢查時(shí),如果具有相同鍵的被插入元組和某個(gè)其他元組都活著,則必須報(bào)告錯(cuò)誤(注意為了這個(gè)目的,“活著”實(shí)際意味著“在索引項(xiàng)的 HOT 鏈上的任何元組都活著”)。要實(shí)現(xiàn)這一點(diǎn),需要給 aminsert 傳遞一個(gè) checkUnique 參數(shù),其中包含下列值之一:

  • UNIQUE_CHECK_NO表明不需要做唯一性檢測(cè)(這不是一個(gè)唯一索引)。

  • 如上所述,UNIQUE_CHECK_YES表明有一個(gè)不可延遲的唯 一索引,并且必須立即做唯一性檢測(cè)。

  • UNIQUE_CHECK_PARTIAL表明唯一性約束是可延遲的。PostgreSQL將會(huì)用這個(gè)模式來插入每一行的索引項(xiàng)。訪問方法必須允許重復(fù)的項(xiàng)進(jìn)入索引,并且通過從 aminsert返回 FALSE 來報(bào)告任何可能的重復(fù)。對(duì)于返回 FALSE 的每一行,將計(jì)劃一個(gè)延遲的重新檢查。

    訪問方法必須能夠標(biāo)識(shí)任何可能違反唯一約束的行,但是對(duì)它來說假陽(yáng)性報(bào)告不是錯(cuò)誤。這樣就允許檢查不用等到其他事務(wù)都結(jié)束;這里報(bào)告的沖突不會(huì)被當(dāng)做錯(cuò)誤來看待,并且隨后將會(huì)被重新檢查,而到那時(shí)它們可能不再是沖突了。

  • UNIQUE_CHECK_EXISTING表明這是一個(gè)行的延遲重新檢查,該行被報(bào)告為一個(gè)潛在的唯一性違背。盡管這會(huì)通過調(diào)用aminsert來實(shí)現(xiàn),在這種情況下這個(gè)訪問方法能插入一個(gè)新索引項(xiàng)。該索引項(xiàng)已經(jīng)存在。 當(dāng)然,訪問方法必須檢查是否有另一個(gè)活著的索引項(xiàng)。如果有,并且如果目標(biāo)行也仍然存活,那么報(bào)告錯(cuò)誤。

    我們推薦,在一個(gè)UNIQUE_CHECK_EXISTING調(diào)用中,訪問方法進(jìn)一步驗(yàn)證目標(biāo)行真的在索引中有一個(gè)現(xiàn)有的項(xiàng),并且如果不是這樣就報(bào)錯(cuò)。這是個(gè)好主意,因?yàn)楸粋鞯?code class="function">aminsert的索引元組值將已經(jīng)被重新計(jì)算過。如果索引定義涉及不是真正不變的函數(shù),我們可能正在檢查索引的錯(cuò)誤區(qū)域。對(duì)重新檢查中找到的目標(biāo)行的檢查會(huì)驗(yàn)證我們正在掃描之前被用于原始插入的同一元組值。

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)