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