為什麼hot_standby_feedback可能產生誤導?
- 2019 年 11 月 21 日
- 筆記
作者簡介
RICHARD YEN: EnterpriseDB公司的高級支持工程師,支撐EnterpriseDB的整個產品。在加入EnterpriseDB之前,Richard曾擔任數據庫工程師和WEB開發人員,主要在實際中操作,關注可擴展性、性能和可恢復性等方面。
譯者簡介
陳雁飛,開源PostgreSQL愛好者,一直從事PostgreSQL數據庫運維工作
李冉,瀚高基礎軟件工具開發工程師。
引言
當我第一次參與Postgres數據庫管理的時候,很快就了解了複製的必要性。我的第一個項目是將數據庫放在Slony上,這是一種熱門的新複製技術,代替笨拙的DRBD配置,並且支持對數據庫近實時的副本備份。當然,隨着時間和規模的增加,Slony很難滿足快速增加的寫入流量,並且最終會受到寫放大的影響(由於所有操作包含對底層操作,因此每次操作最終會變成對數據庫的兩次或者更多次寫)。當在Postgres9.0版本出現流複製的時候,每個人都覺得自己得到了金牌。流複製不僅速度快,而且它利用了Postgres中已經存在的特性:WAL stream。
距離Postgres9.0版本已經過去很多年了(我們馬上見到12版本)。這期間Postgres增加了很多特性,比如熱備份、邏輯複製和一些雙向Matser-Master複製擴展插件。這個是一條不尋常的成長之路,特別是我記得在大約是在2010年的PGcon大會上一次非正式研討的時候(BOF),有人說Postgres的路線圖中將不會包括複製。
在多年來流複製的所有改進中,我認為一個最容易被誤解特性是hot_standby_feedback,因此我希望在這裡能夠澄清一下。
通過流複製,用戶可以建立任意個與主機有克隆複製關係的備服務器,並且這些備服務器也支持一些特定類型的操作。比如OLTP應用程序中只讀流量、龐大的定時任務、以及長時間運行的報表查詢等,所有這些操作不會影響主服務器上的寫入流量。但是,有些人偶爾會遇到查詢由於某種原因被終止了,並且在日誌中,可以看到類似如下內容:
ERROR: canceling statement due to conflict with recovery |
---|
這個錯誤是一個不幸的現實,沒有人會喜歡這個。沒有人希望自己的查詢被取消,就像沒人喜歡在點一個熏牛肉三明治的時候,過了10分鐘才被店家告知熏牛肉賣完了。但是,這種情況確實發生了。通常在流複製中一些數據需要重放的時候發生查詢衝突的情況,常見的是一些舊數據的刪除,無論是刪除,還是刪除表,甚至VACUUM清理死行數據操作。在Google上搜索Postgres流複製的時候,一些用戶建議通過設置hot_standby_feedback參數為on的方式解決遇到的查詢衝突問題。很多人很高興地設置hot_standby_feedback = on,然後業務可以正常運轉了,但是我遇到一些客戶設置之後仍然會從日誌中看到ERROR: canceling statement due to conflict with recovery錯誤信息。
為什麼繼續產生查詢衝突?
關於Postgres的流複製,需要牢記的重要一點:它的目標是創建數據庫副本,並將WAL日誌傳輸到另外一邊去。現在,DBA可能有其他的不同目標,包括在備服務器上進行查詢和報表操作,但是這個並不是Postgres的目標。DBA可以配置一些GUC參數,用於告訴Postgres在某些方面不需要太激進,以便能預留一點空間在備機上運行查詢操作。但是,如果備機沒有處理接收到的WAL流文件,那麼在pg_xlog/pg_wal目錄下的WAL日誌將堆積並膨脹,並且使運行的主服務器面臨磁盤空間耗盡的風險。
hot_standbt_feedback究竟是做什麼的?
這裡不想再談技術細節了,我認為Alexey Lesovsky用一個很好的例子解釋了它,並且有源代碼提供給有興趣的人詳細閱讀。簡而言之,改參數表示備機將相關信息發送給主機(pg_stat_replication.backend_xmin值),從而幫助主機確定哪些dead tuple可以安全地被vacuum清理掉。換句話說,當備機上執行查詢的時候,會通過設置backend_xmin影響主機的元組可見性判斷,從而主機不會vacuum清理相關元組。
-bash-4.1$ psql -p5432 -c "SELECT application_name, backend_start, backend_xmin, state, sent_location, write_location, flush_location, replay_location FROM pg_stat_replication" application_name | backend_start | backend_xmin | state | sent_location | write_location | flush_location | replay_location ——————+———————————+———————–+———–+—————+—————-+—————-+—————– walreceiver | 01-MAR-19 23:18:55.31685 +00:00 |{look ma, nothing here}| streaming | 0/6000060 | 0/6000060 | 0/6000060 | 0/6000060 (1 row) -bash-4.1$ psql -p5433 -c "ALTER SYSTEM SET hot_standby_feedback TO on" ALTER SYSTEM -bash-4.1$ pg_ctl -D /var/lib/pgsql/9.6/standby_pgdata restart waiting for server to shut down…. done server stopped server starting -bash-4.1$ psql -p5432 -c "SELECT application_name, backend_start, backend_xmin, state, sent_location, write_location, flush_location, replay_location FROM pg_stat_replication" application_name | backend_start | backend_xmin | state | sent_location | write_location | flush_location | replay_location ——————+———————————-+———————–+———–+—————+—————-+—————-+—————– walreceiver | 01-MAR-19 23:21:18.373624 +00:00 | {hs_feedback on} 2346 | streaming | 0/6000060 | 0/6000060 | 0/6000060 | 0/6000060 (1 row)
基本上,這是一個延遲策略,將清除信息排除在WAL之外,因為一旦deaded元組被vacuum清理,清理信息將寫入WAL日誌流中並在備機上產生衝突。注意這裡有一點需要權衡考慮:設置hot_standby_feedback=on可能會導致主機上表發生膨脹,通常這個並不是很重要。但是,即使這樣,仍然有一些查詢衝突是hot_standby_feedback無法阻止的:
1、對主機對象上請求排他鎖
2、斷斷續續地walreceiver連接進程
3、對少數表的頻繁修改
排它鎖導致的衝突
如果流複製需要成為一項可靠的技術,那麼所有的備機需要和它們的主數據庫保持一致。這也就是說主機上所有的修改需要儘可能快發送到備機並恢復,這裡需要特別注意的是DDL操作以及其他需要獲取排它鎖的變化。任何時延都可能導致數據庫的不一致(誰會願意看到已經刪除的表?),且當在主機發生故障並且需要立即進行故障轉移的時候,最理想的情況是備機與主機之間是完全同步的。因此,DBA希望能儘可能快的重放WAL日誌。
一致性還意味着,當Alice和Bob在只讀的備機上查詢得到的結果是準確的,和主機上運行查詢結果一樣(當然,除非DBA已經將備機設置在固定時間內跟蹤主機,比如在recovery.conf中設置recovery_min_apply_delay參數)。但是,如果有人在數據庫里執行一個長時間運行的查詢,那麼這個查詢會阻止WAL日誌的重放,因此必須給出一些參數用於取消這些查詢操作。DBA可以設置max_standby_archive_delay或max_standby_streaming_delay參數,以便在取消查詢前給衝突查詢一段時間。這兩個參數在設置的要謹慎,需要考慮到業務允許存在的複製時延。
斷斷續續的walreceiver連接的影響
網絡中斷將斷開walreceiver連接,並最終使得發送給主機的backend_xmin信息失效。也就是說,如果walreceiver連接斷開,那麼在walreceiver重新連接並告訴主機新的backend_xmin信息之前,主機可以自由地對期望的對象進行vacuum清理。從備機用戶角度看,一些非期望的vacuum清理操作可能發生在walreceiver進程重連期間,從而可能導致程出現查詢衝突取消的情況。這種情況可以通過流複製槽來緩解,它可以記錄斷開walreceiver連接的xmin信息。
少量表的頻繁寫入
VACUUM操作通常不是阻塞的,但是如果存在有足夠的查詢/刪除操作,一個vacuum任務可能會發現一個標記為需要刪除的完整頁面,在這種情況它將嘗試獲取對象的排它鎖信息,從而將該頁面從磁盤上刪除,從而減小表佔用的空間。從流複製恢復角度看,這種鎖行為基本上與DDL操作一樣,並且最終可能會導致正在運行中查詢被取消掉。
總結
即使設置了hot_standby_feedback=on,仍然有很多種情況會導致查詢因為衝突而取消,但是這些情況比較少見。查詢pg_stat_database_conflicts信息有助於了解產生取消查詢的原因,對找到緩解該問題的方法有很大幫助。最後,hot_standby_feedback只是眾多處理備機查詢衝突方法中的一種,用戶需要明白的一點是:為了保證一致和可靠,這種查詢衝突時必須的。無論是通過hot_feed_back參數還是其他方式,設置備機查詢為更高優先級,都是以犧牲主備之間複製時延(有時可能會很小)為代價。
原文地址:
https://www.enterprisedb.com/blog/why-hotstandbyfeedback-can-be-misleading
譯後感
本文介紹的是數據庫中備機只讀功能,重點說明了三種情況可能會導致備機上查詢失敗,並介紹了hot_standby_feedback參數並不能完全解決備機查詢衝突問題,最後闡述在流複製下,我們的首要目標是確保數據的一致,而不應該將備機的查詢設置為更高優先級別。
在日常運維中,隨着數據規模的擴大,我們會自然而然的想到使用多個備機進行擴展查詢(特別是現在JDBC和libpq接口已經支持多個連接host設置,更加方便實現一主多備集群操作),但是我們不得不接受備機可能存在的延遲、衝突等情況,這一點需要在使用中特別注意。同時,如果我們要在備機上做邏輯備份,也有可能會存在失敗的情況。即使我們設置了hot_standby_feedback=on,主機上仍然可能存在請求排他鎖的情況,比如autovacuum清理中對頁面的truncate操作(WIP: long transactions on hot standby feedback replica / proof of concept)、每個操作中可能觸發的HOT pruning對頁面的truncate操作(正文中介紹的第三種情況)。因此,如果大家有使用備機查詢功能,一定要結合業務需要和數據可靠性要求,合理設置參數。