你知道你的PG數(shù)據(jù)安全準(zhǔn)確嗎
去年我寫(xiě)過(guò)一篇文章《PG數(shù)據(jù)庫(kù)離企業(yè)級(jí)數(shù)據(jù)庫(kù)還有多遠(yuǎn)》,實(shí)際上對(duì)PG了解得越深入,這個(gè)問(wèn)題就越值得我們?nèi)ニ伎肌G皫滋煲粋€(gè)做數(shù)據(jù)庫(kù)高可用架構(gòu)的朋友在我的公眾號(hào)上留言,說(shuō)在PG數(shù)據(jù)庫(kù)中,如果刪除了某一個(gè)數(shù)據(jù)文件,PG數(shù)據(jù)庫(kù)居然不報(bào)錯(cuò),還能查出數(shù)據(jù)來(lái),不過(guò)查出來(lái)的數(shù)據(jù)是錯(cuò)的。這一點(diǎn)我以前倒是沒(méi)有注意到,數(shù)據(jù)庫(kù)丟失數(shù)據(jù)文件不報(bào)錯(cuò)是正常現(xiàn)象,不過(guò)查詢(xún)數(shù)據(jù)的時(shí)候,如果掃描到了這部分內(nèi)容,按理說(shuō)應(yīng)該是會(huì)報(bào)錯(cuò)的,比如Oracle就是如此。昨天下班前我正好有點(diǎn)時(shí)間,就做了個(gè)小實(shí)驗(yàn)。實(shí)驗(yàn)內(nèi)容有點(diǎn)長(zhǎng),我先講一些結(jié)論性的東西,有興趣了解細(xì)節(jié)的朋友看完結(jié)論性的分析后再去看實(shí)驗(yàn)的詳情吧。
數(shù)據(jù)文件的完整性檢查是一個(gè)開(kāi)銷(xiāo)十分巨大的操作,因此幾乎沒(méi)有數(shù)據(jù)庫(kù)會(huì)隨時(shí)對(duì)數(shù)據(jù)文件的完整性做檢查。連Oracle這種段頁(yè)式結(jié)構(gòu),以表空間為組織模式的數(shù)據(jù)庫(kù)都不會(huì)隨時(shí)去檢查數(shù)據(jù)文件的完整性和可用性。只有在訪(fǎng)問(wèn)某個(gè)數(shù)據(jù)文件的時(shí)候才會(huì)通過(guò)文件頭去做一些校驗(yàn)。不過(guò)對(duì)于數(shù)據(jù)文件中的數(shù)據(jù)的一致性仍然不會(huì)去做檢查。這是一種更大開(kāi)銷(xiāo)的操作。只有訪(fǎng)問(wèn)到相關(guān)數(shù)據(jù)的時(shí)候才會(huì)去做一致性和完整性的檢查(并不是所有的訪(fǎng)問(wèn)操作都會(huì)做)。不過(guò)不管如何,RDBMS系統(tǒng)要盡可能保證查詢(xún)出來(lái)的數(shù)據(jù)的邏輯一致性,確保數(shù)據(jù)一定是正確的。
對(duì)于Oracle這樣的數(shù)據(jù)庫(kù),文件的屬性被記錄在control file中,新增一個(gè)文件或者文件的大小發(fā)生變化的時(shí)候,會(huì)自動(dòng)更新數(shù)據(jù)。對(duì)于PG這樣的每張表都會(huì)有多個(gè)文件來(lái)存儲(chǔ)數(shù)據(jù)的數(shù)據(jù)庫(kù)來(lái)說(shuō),登記每個(gè)使用過(guò)的文件是一種十分高成本的操作,一個(gè)上TB的表可能就會(huì)擁有上千個(gè)數(shù)據(jù)文件。這是一種一致性對(duì)于性能的妥協(xié),這種妥協(xié)為PG數(shù)據(jù)庫(kù)的數(shù)據(jù)一致性帶來(lái)了巨大的隱患。昨天我的實(shí)驗(yàn)的結(jié)論是:“當(dāng)PG數(shù)據(jù)文件出現(xiàn)丟失的時(shí)候,PG數(shù)據(jù)庫(kù)不一定會(huì)因?yàn)槲募G失而報(bào)錯(cuò),而是會(huì)直接返回錯(cuò)誤的數(shù)據(jù)”。這是一種十分恐怖的特性,對(duì)于關(guān)鍵性的企業(yè)級(jí)應(yīng)用來(lái)說(shuō),錯(cuò)誤的數(shù)據(jù)比丟失數(shù)據(jù)還要可怕。
下面請(qǐng)大家看我的實(shí)驗(yàn)過(guò)程,我使用的PG版本是12.6,如果PG數(shù)據(jù)庫(kù)在新版本中已經(jīng)修復(fù)了我今天實(shí)驗(yàn)中的問(wèn)題,也請(qǐng)朋友留言告知。先創(chuàng)建一張表,寫(xiě)入部分?jǐn)?shù)據(jù)。從pg_class里可以看到relfilenode是16399。
圖片
圖片
可以看出目前數(shù)據(jù)文件存儲(chǔ)在3個(gè)文件里。同時(shí)有一個(gè)fsm文件記錄了空閑空間的情況。我們做個(gè)簡(jiǎn)單的count查詢(xún)。
圖片
沒(méi)錯(cuò),我剛才寫(xiě)入了1000萬(wàn)條數(shù)據(jù)。然后我們開(kāi)始作妖,刪除16399.1文件看看會(huì)出現(xiàn)什么情況。
圖片
在另外一個(gè)窗口用rm命令刪除了文件后,我們查一下這張表的數(shù)據(jù):
圖片
這里是報(bào)錯(cuò)了,確實(shí)發(fā)現(xiàn)了剛才被刪除的文件丟失了。在多次實(shí)驗(yàn)中,我發(fā)現(xiàn)有時(shí)候不會(huì)報(bào)錯(cuò),可以直接成功。我們先不管不報(bào)錯(cuò)的場(chǎng)景,后面我會(huì)補(bǔ)充這方面的數(shù)據(jù),現(xiàn)在我們重啟一下數(shù)據(jù)庫(kù),再來(lái)看看。
數(shù)據(jù)庫(kù)重啟后,居然查詢(xún)成功了,只不過(guò)數(shù)據(jù)似乎不太對(duì),少了一些數(shù)據(jù),而且少的還不是16399.1里的所有數(shù)據(jù),似乎重啟數(shù)據(jù)庫(kù)的時(shí)候做了RECOVER。
圖片
回到目錄中再去查看一下。十分奇怪,剛才被刪除的文件又回來(lái)了。
圖片
從數(shù)據(jù)庫(kù)的日志中我們可以看到,RDBMS是做了RECOVER,自動(dòng)恢復(fù)了被刪除的文件。不過(guò)這個(gè)恢復(fù)并不完整,但是系統(tǒng)也沒(méi)有報(bào)錯(cuò),這種機(jī)制會(huì)給我們帶來(lái)錯(cuò)覺(jué),導(dǎo)致業(yè)務(wù)數(shù)據(jù)的錯(cuò)亂,是十分可怕的。于是我再做一次刪除,然后重啟數(shù)據(jù)庫(kù)試試。十分奇怪的是,這回?cái)?shù)據(jù)庫(kù)重啟沒(méi)有像上回那樣RECOVER了丟失的數(shù)據(jù)文件,這種行為的不確定性也說(shuō)明了PG數(shù)據(jù)庫(kù)在數(shù)據(jù)一致性檢查方面存在一定的缺陷,不能保持某些恢復(fù)行為的一致性,對(duì)于企業(yè)級(jí)數(shù)據(jù)庫(kù)來(lái)說(shuō),這也是十分致命的。
圖片
圖片
從文件系統(tǒng)上看,這回丟失的文件也沒(méi)有恢復(fù)。
圖片
我再查詢(xún)數(shù)據(jù),發(fā)現(xiàn)丟失了一半的數(shù)據(jù),只有一半數(shù)據(jù)了。接下來(lái)再試試CTAS,完整拷貝這張表的全部數(shù)據(jù)。這個(gè)操作居然成功完成,沒(méi)有任何報(bào)錯(cuò),這說(shuō)明RDBMS認(rèn)為當(dāng)前的數(shù)據(jù)是完整的,而實(shí)際上數(shù)據(jù)已經(jīng)產(chǎn)生了嚴(yán)重的丟失。接下來(lái)測(cè)試下寫(xiě)入數(shù)據(jù),這個(gè)測(cè)試也成功了。
圖片
這是一個(gè)十分恐怖的實(shí)驗(yàn),在我的理解里,F(xiàn)SM里起碼會(huì)記錄空塊的情況,丟失文件的問(wèn)題應(yīng)該能從查找FSM文件的時(shí)候被發(fā)現(xiàn)。不過(guò)這沒(méi)有發(fā)生,不過(guò)也很容易理解,因?yàn)镮NSERT數(shù)據(jù)的時(shí)候,只需要查空塊就可以了,不一定會(huì)發(fā)現(xiàn)問(wèn)題。目前我還沒(méi)有從PG的源碼上去分析這個(gè)問(wèn)題,因此還不是很清晰這方面的機(jī)理。不過(guò)從目前的實(shí)驗(yàn)上看,PG確實(shí)存在誤刪文件后不會(huì)被發(fā)現(xiàn),導(dǎo)致數(shù)據(jù)出現(xiàn)錯(cuò)誤的問(wèn)題。這種缺陷是十分恐怖的,很多時(shí)候不怕丟數(shù)據(jù),而是怕丟了數(shù)據(jù)你不知道。因?yàn)閷?duì)于核心業(yè)務(wù)系統(tǒng)來(lái)說(shuō),數(shù)據(jù)準(zhǔn)確性是最為關(guān)鍵的。
這回我們換一個(gè)玩法,首先我們創(chuàng)建好表數(shù)據(jù),然后我們關(guān)閉數(shù)據(jù)庫(kù),再刪除某個(gè)文件。重啟數(shù)據(jù)庫(kù)。
圖片
上圖中黃線(xiàn)后面的操作是我重啟數(shù)據(jù)庫(kù)后做的,發(fā)現(xiàn)被刪除的文件沒(méi)有被恢復(fù)。再看看查詢(xún)結(jié)果。
圖片
如預(yù)期的那樣,沒(méi)有報(bào)錯(cuò),但是結(jié)果是錯(cuò)誤的,少了400多萬(wàn)條數(shù)據(jù)。剛才刪除文件前我備份了該文件,把該文件直接拷貝回來(lái)看看。
圖片
仍然沒(méi)有報(bào)錯(cuò),正確的結(jié)果回來(lái)了,是不是很神奇?。。?。從上面的實(shí)驗(yàn)可以看到,如果在PG數(shù)據(jù)庫(kù)中丟失某個(gè)數(shù)據(jù)文件,那么數(shù)據(jù)庫(kù)的行為可能是不確定的,不過(guò)大概率會(huì)給你返回錯(cuò)誤的數(shù)據(jù)。這種特性會(huì)對(duì)于關(guān)鍵的企業(yè)級(jí)應(yīng)用帶來(lái)困難。因此我們必須在盡可能不影響數(shù)據(jù)庫(kù)性能的前提下彌補(bǔ)這個(gè)缺陷。至于如何彌補(bǔ),可能需要對(duì)源代碼做一些解讀后才能想辦法。今天的實(shí)驗(yàn)先到這里吧,源代碼的解讀隨后有時(shí)間再做。有興趣的朋友也可以去閱讀分析一下。