PostgreSQL 應(yīng)用級(jí)別的數(shù)據(jù)完整性檢查

2021-08-27 15:26 更新
13.4.1. 用可序列化事務(wù)來(lái)強(qiáng)制一致性
13.4.2. 使用顯式鎖定強(qiáng)制一致性

對(duì)于使用讀已提交事務(wù)的數(shù)據(jù)完整性強(qiáng)制業(yè)務(wù)規(guī)則非常困難,因?yàn)閷?duì)每一個(gè)語(yǔ)句數(shù)據(jù)視圖都在變化,并且如果一個(gè)寫沖突發(fā)生即使一個(gè)單一語(yǔ)句也不能把它自己限制到該語(yǔ)句的快照。

雖然一個(gè)可重復(fù)讀事務(wù)在其執(zhí)行期間有一個(gè)穩(wěn)定的數(shù)據(jù)視圖,在使用MVCC快照進(jìn)行數(shù)據(jù)一致性檢查時(shí)也有一個(gè)小問(wèn)題,它涉及到被稱為讀/寫沖突的東西。如果一個(gè)事務(wù)寫數(shù)據(jù)并且一個(gè)并發(fā)事務(wù)嘗試讀相同的數(shù)據(jù)(不管是在寫之前還是之后),它不能看到其他事務(wù)的工作。讀取事務(wù)看起來(lái)是第一個(gè)執(zhí)行的,不管哪個(gè)是第一個(gè)啟動(dòng)或者哪個(gè)是第一個(gè)提交。如果就到此為止,則沒(méi)有問(wèn)題,但是如果讀取者也寫入被一個(gè)并發(fā)事務(wù)讀取的數(shù)據(jù),現(xiàn)在有一個(gè)事務(wù)好像是已經(jīng)在前面提到的任何一個(gè)事務(wù)之前運(yùn)行。如果看起來(lái)最后執(zhí)行的事務(wù)實(shí)際上第一個(gè)提交,在這些事務(wù)的執(zhí)行順序圖中很容易出現(xiàn)一個(gè)環(huán)。當(dāng)這樣一個(gè)環(huán)出現(xiàn)時(shí),完整性檢查在沒(méi)有任何幫助的情況下將不會(huì)正確地工作。

正如第 13.2.3 節(jié)中提到的,可序列化事務(wù)僅僅是可重復(fù)讀事務(wù)增加了對(duì)讀/寫沖突的危險(xiǎn)模式的非阻塞監(jiān)控。當(dāng)檢測(cè)到一個(gè)可能導(dǎo)致表面的執(zhí)行順序中產(chǎn)生環(huán)的模式,涉及到的一個(gè)事務(wù)將被回滾來(lái)打破該環(huán)。

13.4.1. 用可序列化事務(wù)來(lái)強(qiáng)制一致性

如果可序列化事務(wù)隔離級(jí)別被用于所有需要一個(gè)一致數(shù)據(jù)視圖的寫入和讀取,不需要其他的工作來(lái)保證一致性。在PostgreSQL中,來(lái)自于其他環(huán)境的被編寫成使用可序列化事務(wù)來(lái)保證一致性的軟件應(yīng)該只工作在這一點(diǎn)上。

當(dāng)使用這種技術(shù)時(shí),如果應(yīng)用軟件通過(guò)一個(gè)框架來(lái)自動(dòng)重試由于序列化錯(cuò)誤而回滾的事務(wù),它將避免為應(yīng)用程序員帶來(lái)不必要的負(fù)擔(dān)。把default_transaction_isolation設(shè)置為serializable可能是個(gè)好主意。通過(guò)觸發(fā)器中的事務(wù)隔離級(jí)別檢查來(lái)采取某些動(dòng)作來(lái)保證沒(méi)有其他事務(wù)隔離級(jí)別被使用(由于疏忽或者為了破壞完整性檢查)也是明智的。

性能建議見(jiàn)第 13.2.3 節(jié)。

警告

這個(gè)級(jí)別的使用可序列化事務(wù)的完整性保護(hù)還沒(méi)有擴(kuò)展到熱備份模式(第 26.5 節(jié))。由于這個(gè)原因,那些使用熱備份的系統(tǒng)可能想要在主控機(jī)上使用可重復(fù)讀和顯式鎖定。

13.4.2. 使用顯式鎖定強(qiáng)制一致性

當(dāng)可以使用非可序列化寫時(shí),要保證一行的當(dāng)前有效性并保護(hù)它不受并發(fā)更新的影響,我們必須使用SELECT FOR UPDATE、SELECT FOR SHARE或一個(gè)合適的LOCK TABLE 語(yǔ)句(SELECT FOR UPDATESELECT FOR SHARE鎖只針對(duì)并發(fā)更新返回行,而 LOCK TABLE會(huì)鎖住整個(gè)表)。當(dāng)從其他環(huán)境移植應(yīng)用到PostgreSQL時(shí)需要考慮這些。

關(guān)于這些來(lái)自其他環(huán)境的轉(zhuǎn)換還需要注意的是SELECT FOR UPDATE不保證一個(gè)并發(fā)事務(wù)將不會(huì)更新或刪除一個(gè)被選中的行。要在PostgreSQL中這樣做,你必須真正地更新該行,即便沒(méi)有值需要被改變。SELECT FOR UPDATE 臨時(shí)阻塞其他事務(wù),讓它們不能獲取該相同的鎖或者執(zhí)行一個(gè)會(huì)影響被鎖定行的 UPDATEDELETE,但是一旦正持有該所鎖的事務(wù)提交或回滾,一個(gè)被阻塞的事務(wù)將繼續(xù)執(zhí)行沖突操作,除非當(dāng)鎖被持有時(shí)一個(gè)該行的實(shí)際UPDATE被執(zhí)行。

在非可序列化MVCC環(huán)境下,全局有效性檢查需要一些額外的考慮。例如,一個(gè)銀行應(yīng)用可能會(huì)希望檢查一個(gè)表中的所有扣款總和等于另外一個(gè)表中的收款總和,同時(shí)兩個(gè)表還會(huì)被更新。比較兩個(gè)連續(xù)的在讀已提交模式下不會(huì)可靠工作的SELECT sum(...)命令, 因?yàn)榈诙€(gè)查詢很可能會(huì)包含沒(méi)有被第一個(gè)查詢考慮的事務(wù)提交的結(jié)果。在一個(gè)單一的可重復(fù)讀事務(wù)里進(jìn)行兩個(gè)求和則給出在可串行化事務(wù)開始之前提交的所有事務(wù)產(chǎn)生的準(zhǔn)確結(jié)果 — 但有人可能會(huì)合理地置疑在結(jié)果被遞交的時(shí)候,它們是否仍然相關(guān)。 如果可重復(fù)讀事務(wù)本身在嘗試做一致性檢查之前應(yīng)用了某些變更,那么檢查的有用性就更加值得討論了, 因?yàn)楝F(xiàn)在它包含了一些(但不是全部)事務(wù)開始后的變化。 在這種情況下,一個(gè)小心的人可能希望鎖住所有需要檢查的表,這樣才能獲得一個(gè)無(wú)可置疑的當(dāng)前現(xiàn)狀的圖像。 一個(gè)SHARE模式(或者更高)的鎖保證在被鎖定表中除了當(dāng)前事務(wù)所作的更改之外,沒(méi)有未提交的更改。

還要注意如果某人正在依賴顯式鎖定來(lái)避免并發(fā)更改,那么他應(yīng)該使用讀已提交模式, 或者是在可重復(fù)讀模式里在執(zhí)行命令之前小心地獲取鎖。 在可重復(fù)讀事務(wù)里獲取的鎖保證了不會(huì)有其它修改該表的事務(wù)正在運(yùn)行,但是如果事務(wù)看到的快照在獲取鎖之前, 那么它可能早于表中一些現(xiàn)在已經(jīng)提交的更改。 一個(gè)可重復(fù)讀事務(wù)的快照實(shí)際上是在它的第一個(gè)查詢或者數(shù)據(jù)修改命令(SELECT、INSERTUPDATEDELETE)開始的時(shí)候凍結(jié)的,因此我們可以在快照凍結(jié)之前顯式地獲取鎖。


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

掃描二維碼

下載編程獅App

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

編程獅公眾號(hào)