說明: sql
如下大部份內容來自 《PostgreSQL實戰》 mvc
PG流複製場景下,默認配置下, 若是在PG從庫執行長時間的查詢,會出現查詢的報錯。提示dom
ERROR: canceling statement due to conflict with recoveryide
DETAIL: User query might have needed to see row versions that must be removed.post
根據報錯信息,在主庫上執行長時間查詢過程當中,因爲此查詢涉及的記錄有可能在主庫上被更新或刪除,根據 PostgreSQL的mvcc機制,更新或刪除的數據不是當即從物理塊上刪除,而是以後autovacuum進程對老版本數據進行 VACUUM,主庫上對更新或刪除數據的老版本進行 VACUUM後,從庫上也會執行這個操做,從而與從庫當前查詢產生衝突,致使查詢被中斷並拋出以上錯誤。測試
實際上 PostgreSQL提供了配置參數來減小或避免這種狀況出現的機率,主要包括如下兩個參數:spa
maxstandby_ streaming_delay:進程
此參數默認爲30秒,當備庫執行SQL時,有可能與正在應用的WAL發生衝突,此查詢若是30秒沒有執行完成則被停止,注意30秒不是備庫上單個查詢容許的最大執行時間,是指當備庫上應用WAL時容許的最大WAL延遲應用時間,所以備庫上查詢的執行時間有可能不到這個參數設置的值就被停止了,此參數能夠設置成-1,表示當從庫上的WAL應用進程與從庫上執行的查詢衝突時,WAL應用進程一直等待直到從庫查詢執行完成。事務
hotstandby_feedback:資源
默認狀況下從庫執行查詢時並不會通知主庫,設置此參數爲on後從庫執行查詢時會通知主庫,當從庫執行查詢過程當中,主庫不會清理從庫須要的數據行老版本,所以,從庫上的查詢不會被停止,然而,這種方法也會帶來必定的弊端,主庫上的表可能出現膨脹,主庫表的膨脹程度與表上的寫事務和從庫執行時間有關,此參數默認爲off
案例:
CentOS7.5+PG版本11.5
pgMaster 爲主庫
pgSlave 爲備庫
調整備庫的參數,設置
max_standby_streaming_delay = 10s # (測試便於看出效果這個參數調的比較低)
hot_standby_feedback = off
而後reload下PG的配置使其生效
在主庫pgMaster 上建立測試表:
\c postgres
create table test_per2 ( id int , flag int);
insert into test_per2 (id) select * from generate_series(1,1000000) ;
編寫pgbench壓測腳本 update_per2.sql 內容以下:
\set v_id random(1,1000000)
update test_per2 set flag='1' where id=:v_id;
開始壓測:
pgbench -c 8 -T 120 -d postgres -Upostgres -n N -M prepared -f update_per2.sql
而後,到pgSlave備庫去執行下查詢操做:
postgres=# select pg_sleep(12),* from test_per2 limit 10 ;
ERROR: canceling statement due to conflict with recovery
DETAIL: User query might have needed to see row versions that must be removed.
Time: 729.120 ms
這裏,能夠很容易就復現了這個報錯場景。
解決方法有2種:
方案一、 調大 max_standby_streaming_delay 參數值
咱們能夠將max_standby_streaming_delay 調整爲-1 繞開這個錯誤,或者將這個值調大些。
例如將備庫的參數max_standby_streaming_delay調整爲120s:
max_standby_streaming_delay = 120s
hot_standby_feedback = off
而後 使用 pg_ctl reload 使其生效
而後,再次到pgSlave備庫去執行下查詢操做,能夠看到查詢能夠正常執行了:
postgres=# select pg_sleep(12), id ,flag from test_per2 limit 2 ;
pg_sleep | id | flag
----------+----+------
| 1 | NULL
| 2 | NULL
(2 rows)
方案二、 開啓 hot_standby_feedback 參數
hot_standby_feedback 參數設置爲on後,從庫執行查詢時會通知主庫,從庫執行大查詢過程當中,主庫不會清理從庫須要用到的數據行老版本。
備庫上須要開啓的參數:
max_standby_streaming_delay = 10s
hot_standby_feedback = on # 主要是這個參數設置爲on便可
而後 使用 pg_ctl reload 使其生效
這時候,到備庫去查詢,能夠發現能查詢成功:
postgres=# select pg_sleep(2), id ,flag from test_per2 limit 2 ;
pg_sleep | id | flag
----------+----+------
| 1 | NULL
| 2 | NULL
(2 rows)
postgres=# select pg_sleep(12), id ,flag from test_per2 limit 2 ;
pg_sleep | id | flag
----------+----+------
| 1 | NULL
| 2 | NULL
(2 rows)
上面的2種方式中,都是有不太好的地方:
一、 設置 max_standby_streaming_delay 參數爲-1,這種方式有可能備庫上慢查詢因爲長時間執行而消耗大量主機資源,建議根據應用狀況設置一個較合理的值
二、 設置 hot_standby_feedback=on,這種方式可能會使主庫某些表產生膨脹。
這兩種方式不管選擇哪個都應該增強對流複製主庫、備庫慢查詢的監控,並分析是否須要人工介入維護。