公司有一臺pg從資料庫,連線的是位於上海的主庫,最近使用過程中遇到了一個報錯:
ERROR: canceling statement due to conflict with recovery
Detail: User query might have needed to see row versions that must be removed
複製程式碼
原因分析
Google一番後,總結原因如下:
Pg屬於經典的MVCC(multi version concurrency control)架構,在這種模式下,更新一行不是直接修改那一行的內容,而是會建立一個新的行,對應的這兩行屬於不同的版本,這也就是MVCC中multi version的含義。顯然,如果這麼一直建立新的行,磁碟遲早會被撐爆,所以pg內部會有一個 vacuum程式,專門用來清理老資料。
Pg每個row都有兩個特殊的欄位xmin,xmax
- xmin表示row建立時的transaction id
- xmax表示row刪除時的transaction id,如果還沒有被刪除,就是NULL 在一個事務裡面,只能看到一部分資料,還有一部分是不可見的。那麼 pg是如何判斷對一個事務,哪些資料是可見的,哪些資料是不可見的呢? 簡單來說,可以通過下面這個偽函式來判斷:
簡單來說,也就是如果該row建立時的transaction id大於當前事務的transaction id,那麼將不可見;如果該row刪除時的transaction id小於當前事務的transaction id,也不可見。
MVCC目的是提高併發度,讀操作不會阻塞寫操作,寫操作也不會阻止讀操作。
這一特性和從資料庫放在一起的時候,會遇到一些問題: 比如在從資料庫上有一個耗時很長的查詢操作,在查詢的過程中,一些需要的row在主資料庫上面已經被更新或者刪除了。主資料庫並不知道當前在從資料庫在進行一個查詢,它覺得自己可以vacuum掉那些比較老的資料行。對於從資料,他必須replay這個vacuum操作,所以他必須取消所有結果包含這些資料行的查詢。
解決方案
方案1: hot_standby_feedback = on
預設情況下,主資料庫是看不到從資料庫上的查詢操作的,設定hot_standby_feedback為on,可以防止主資料庫的vacuum操作把從資料庫查詢需要的資料行給清除掉。
但是這個方案可能會影響主資料庫的效能,因為主資料庫現在得考慮從資料庫的查詢,勢必會是一種負擔,如在pg官方文件中寫道:
Well, the disadvantage of it is that the standby can bloat the master, which might be surprising to some people, too
複製程式碼
方案2:max_standby_streaming_delay
pg中有一個max_standby_streaming_delay引數,意思是從資料庫replay主資料庫操作可以等待的最長時間。預設是30s,將其設定為一個相對比較大的數,或者-1,也就是可以無限等待,可以在所有查詢完成之後再replay主資料庫的操作,這樣長查詢就不會被取消了。
這個引數相比hot_standby_feedback,只會影響從服務區,對主服務區沒影響,但是有可能會加大主從資料不一致。
可見,從資料庫長耗時查詢並沒有一個十全十美的方案,這也算是一個困擾社群已久的難題。
關注我的微信公眾號