?
本文檔使用 php中文網(wǎng)手冊(cè) 發(fā)布
索引訪問(wèn)方法必須支持多個(gè)進(jìn)程對(duì)索引的并發(fā)更新。在索引掃描期間, PostgreSQL 核心系統(tǒng)在索引上抓取 AccessShareLock, 并且在更新索引期間(包括 VACUUM)也會(huì)抓取RowExclusiveLock 。因?yàn)檫@些鎖類型不會(huì)沖突,所以訪問(wèn)方法有責(zé)任處理任何它自己需要的更細(xì)致 的鎖需求。在全部索引上的排他鎖將只是在索引的創(chuàng)建、刪除或REINDEX 的時(shí)候被使用。
創(chuàng)建一個(gè)支持并發(fā)更新的索引類型通常要求對(duì)所需的行為進(jìn)行廣泛并且細(xì)致的分析。 對(duì)于b-tree 和 Hash索引類型,你可以讀取在src/backend/access/ nbtree/README和src/backend/access/hash/README里面的設(shè)計(jì) 決策。
除了索引自己內(nèi)部的一致性要求之外,并發(fā)更新創(chuàng)建了一些有關(guān)父表(堆)和索引之 間的一致性問(wèn)題。因?yàn)?PostgreSQL是把堆的訪問(wèn)和 更新與索引的訪問(wèn)和更新分開(kāi)的,所以存在一些窗口,在這些窗口里索引可能會(huì)與堆 不一致。我們用下面的規(guī)則處理這樣的問(wèn)題:
在制作一行的索引記錄之前,先做堆記錄。(因此并發(fā)的索引掃描很可能看不 到堆記錄。這么做應(yīng)該是 OK 的,因?yàn)樗饕x者對(duì)未授權(quán)的行不感興趣。不過(guò) 需要看看節(jié)49.5Section 51.5。)
如果一條堆記錄要被刪除(通過(guò)“清空”命令),所有其索引記錄都必須首先刪除。
一次索引掃描必須在保存有amgettuple
最后返回的索引頁(yè)面上維護(hù)一個(gè)銷,
而ambulkdelete
不能刪除其它后端用銷固定的索引頁(yè)面里面的記錄。
下面會(huì)解釋需要這條規(guī)則的原因。
沒(méi)有第三條規(guī)則,如果一個(gè)索引是并發(fā)的,那么一個(gè)索引讀者是可能在一條索引記
錄被 VACUUM刪除之前看到它的,然后在VACUUM刪除它之
后找到其對(duì)應(yīng)的堆記錄。如果讀者到達(dá)該項(xiàng)時(shí),該項(xiàng)編號(hào)仍然沒(méi)有使用,那么這種
情況不會(huì)導(dǎo)致嚴(yán)重的問(wèn)題,因?yàn)榭盏捻?xiàng)槽位會(huì)被 heap_fetch()
忽略。
但是如果第三個(gè)后端已經(jīng)為其它什么東西復(fù)用了這個(gè)項(xiàng)槽位又如何?如果使用MVCC
兼容的快照,那么就不會(huì)有問(wèn)題,因?yàn)樾抡紦?jù)的槽位當(dāng)然是太新了,因而無(wú)法通過(guò)
快照測(cè)試。但是,對(duì)于非 MVCC 兼容的快照(比如 SnapshotNow),那
么就有可能接受并返回一個(gè)實(shí)際上并不匹配掃描鍵字的行??梢酝ㄟ^(guò)要求掃描鍵字
在所有場(chǎng)合下都重新檢查的方法來(lái)避免這種情況,但是這種方法開(kāi)銷太大了。取而
代之的是,通過(guò)在索引頁(yè)面上使用一個(gè)銷,當(dāng)作一個(gè)代理,告訴系統(tǒng)說(shuō),讀者可能
還在對(duì)應(yīng)堆記錄的索引記錄上空"飛行"。在這樣的銷上面進(jìn)行 ambulkdelete
塊確保 VACUUM 無(wú)法在讀者完成讀取之前刪除堆記錄.
這種解法增加了一點(diǎn)點(diǎn)運(yùn)行時(shí)開(kāi)銷,而只是在非常罕見(jiàn)的實(shí)際有沖突的情況下才導(dǎo)致阻塞開(kāi)銷。
這個(gè)解決方法要求索引掃描必須是"同步的":必須在掃描完對(duì)應(yīng)的索引記錄之后馬上 抓取每個(gè)堆記錄。這樣的方案開(kāi)銷比較大,原因有若干個(gè)。而"異步的"掃描,可以先 從索引里收集很多 TID ,然后再稍后的某時(shí)訪問(wèn)堆行,這樣就會(huì)燒很多索引鎖的開(kāi) 銷,以及可以允許更有效的堆訪問(wèn)模式。但是按照上面的分析,在非 MVCC 兼容單塊 著上必須使用同步方法,而對(duì)使用 MVCC 快照的查詢,使用異步掃描應(yīng)該是可以實(shí)現(xiàn)的。
在amgetmulti
索引掃描里,訪問(wèn)方法不需要保證在任何返回的行上
保持一個(gè)銷。畢竟,除了給最后一個(gè)行加銷之外,也沒(méi)法給其它的加。
因此,只能在MVCC兼容的快照里使用這樣的掃描。