PG從庫查詢被終止的解決辦法

說明: 

以下大部分內容來自 《PostgreSQL實戰》 



PG流複製場景下,默認配置下, 如果在PG從庫執行長時間的查詢,會出現查詢的報錯。提示

ERROR: canceling statement due to conflict with recovery

DETAIL: User query might have needed to see row versions that must be removed.


根據報錯信息,在主庫上執行長時間查詢過程中,由於此查詢涉及的記錄有可能在主庫上被更新或刪除,根據 PostgreSQLmvcc機制,更新或刪除的數據不是立即從物理塊上刪除,而是之後autovacuum進程對老版本數據進行 VACUUM,主庫上對更新或刪除數據的老版本進行 VACUUM,從庫上也會執行這個操作,從而與從庫當前查詢產生衝突,導致查詢被中斷並拋出以上錯誤。

 

實際上 PostgreSQL提供了配置參數來減少或避免這種情況出現的概率,主要包括以下兩個參數:

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

然後reloadPG的配置使其生效

 

在主庫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種:

方案1、  調大 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) 

 

方案2、  開啓 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種方式中,都是有不太好的地方:

1、  設置 max_standby_streaming_delay 參數爲-1,這種方式有可能備庫上慢查詢由於長時間執行而消耗大量主機資源,建議根據應用情況設置一個較合理的值

2、  設置 hot_standby_feedback=on,這種方式可能會使主庫某些表產生膨脹。

這兩種方式無論選擇哪一個都應該加強對流複製主庫、備庫慢查詢的監控,並分析是否需要人工介入維護。

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章