天天看點

MySQL核心月報 2015.02-PgSQL · 特性分析· Replication Slot

由于大家目前主要用的還隻是pg自帶的實體複制方式,我們就重點分析一下physical replication slot。

使用physical replication slot,可以達到兩個效果:

a)可以確定從庫(standby)需要的日志不被過早備份出去而導緻從庫失敗,出現下面的error:

通過replication slot記錄的從庫狀态,pg會保證從庫還沒有apply的日志,不會從主庫的日志目錄裡面清除或archive掉。而且,replication slot的狀态資訊是持久化儲存的,即便從庫斷掉或主庫重新開機,這些資訊仍然不會丢掉或失效。

b)當允許應用連接配接從庫做隻讀查詢時,replication slot可以與運作時參數hot_standby_feedback配合使用,使主庫的vacuum操作不會過早的清掉從庫查詢需要的記錄,而出現如下錯誤(錯誤的原因下面會詳細解釋):

下面看看physical replication slot的用法和核心實作。

<b>用法</b>

下面是啟用replication slot的步驟,很簡單:

1)首先需要配置好steaming replication的主庫從庫。涉及的參數有,listen_addresses(='*'),hot_standby(=on), wal_level(=hot_standby),max_wal_senders(=1),尤其注意配置max_replication_slots大于等于1。這些參數主從庫應一緻。

2)在主庫建立replication slot:

此時replication slot還不處于active狀态。

3) 在從庫配置recovery.conf如下,然後重新開機從庫:

4)觀察主庫replication slot的狀态變化:

5)與hot_standby_feedback配合使用。在将從庫的postgresql.conf檔案中的hot_standby_feedback選項設為on,重新開機從庫即可。

<b>核心實作</b>

這個patch改的檔案不少,分析這些代碼,我們重點關注下面的問題:

a)replication slot是如何在核心中建立的?

通過分析建立replication slot時調用的函數replicationslotcreate,可以看出,<b>replication slot實質上是記憶體中的一些資料結構,加上持久化儲存到pg_replslot/&lt;slot name&gt;目錄中的二進制狀态檔案。</b>在pg啟動的時候,預先在共享記憶體中配置設定好這些資料結構所用記憶體(即一個大小為max_replication_slots的數組)。這些資料結構在使用者建立replication slot時開始被使用。一個replication slot被建立并使用後,其資料結構和狀态檔案會被wal(write-ahead-log)的發送者(wal_sender)程序更新。

另外,如果單純從replication slot的名字,我們很容易覺得replication slot會建立新的與從庫的連接配接,進行日志發送。實際上,建立過程中并沒有建立新的與從庫的連接配接,replication slot還是使用了wal_sender原有連接配接(由于一個從庫一個wal_sender連接配接,是以一個從庫和主庫之間也隻有一個active的replication slot)。

b) replication slot的狀态是如何被更新的?

很容易發現,replication slot的狀态的更新有兩種情況。

第一種是在processstandbyhsfeedbackmessage這個函數被更新。這個函數是在處理wal_sender所收到的從庫發回的feedback reply message時調用的。通過這個函數,我們可以看出,每個wal_sender程序的replication slot(就是使用者建立的replication slot)儲存在myreplicationslot這個全局變量中。在處理從庫發回的reply時,reply中的xmin資訊會被提取出來,存入slot的data.xmin和effective_xmin域中,并通過函數procarraysetreplicationslotxmin,最終更新到系統全局的procarray-&gt;replication_slot_xmin結構中(以使其對所有程序可見),完成slot的更新。

這裡要注意,如果我們有多個replication slot(分别對應各自從庫),則在更新全局結構procarray-&gt;replication_slot_xmin時,會選取所有slot中最小的xmin值。

第二種是在processstandbyreplymessage中。這個函數處理從庫發送的restart lsn資訊(即從庫apply的日志的編号),會直接将其更新到replication slot的restart lsn域中,并儲存到磁盤,用于主庫判斷是否要保留日志不被archive。

c) replication slot如何和hot_standby_feedback配合,來避免從庫的查詢沖突的?

這裡,從庫的查詢沖突指的是下面的情況:從庫上有正在運作的查詢,而且運作時間很長;這時主庫上在做正常的vaccum,清除掉無用的記錄版本。但主庫的vaccum是不知道從庫的查詢存在的,是以在清除時,不考慮從庫的正在運作的查詢,隻考慮主庫裡面的事務狀态。其結果,vacuum可能會清除掉從庫查詢中涉及的,仍然在使用的記錄版本。當這些vaccum操作,通過日志同步到從庫,而恰好從庫的查詢仍然沒有運作完,vaccum就要等待或cancel這個查詢,以保證同步正常繼續和查詢不出現錯誤的結果。這樣,每當使用者在從庫運作長查詢,就容易出現我們上面提到到query conflict error。

如何避免這種沖突呢?目前最好的解決方案是使用hot_standby_feedback + replication slot。其原理簡單說就是,從庫将它的查詢所依賴的記錄版本的資訊,以一個事務id來表示,并放在從庫發回給主庫wal_sender的reply中發給主庫(見函數xlogwalrcvsendhsfeedback),并最終傳導給主庫vaccum,讓其刀下留人,暫時不清除相關記錄。

具體過程是,在從庫,函數xlogwalrcvsendhsfeedback調用getoldestxmin獲得xmin,放入給主庫的reply中。主庫的wal_sender收到後,如果使用了replication slot,就把這個xmin放入slot的狀态資訊中,并更新此時系統所有slot的最小xmin。這個系統所有slot的最小xmin怎麼在主庫傳導給vacuum的呢?以自動觸發的vacuum操作為例,其中的邏輯的順序如下:

getsnapshotdata(vacuum事務開始時,擷取slot xmin,存入全局變量) -&gt;vacuum_set_xid_limits(調用 getoldestxmin,通過全局變量,擷取系統xmin和slot xmin,取較小值)-&gt; vacuum_lazy (使用xmin,判斷哪些記錄版本可以清除)

這樣,利用replication slot這個管道,就解決了從庫查詢沖突。

<b>注意事項</b>

最後,介紹一下使用replication slot的注意事項:

1)如果收不到從庫的reply,replication slot的狀态restart lsn會保持不變,造成主庫會一直保留本地日志,可能導緻日志磁盤滿。是以應該實時監控日志磁盤使用情況,并設定較小的wal_sender_timeout,及早發現從庫斷掉的情況。

2)将hot_standby_feedback設為on時,注意如果從庫長時間有慢查詢發生,可能導緻發回到主庫的xmin變化較慢,主庫的vaccum操作停滞,造成主庫被頻繁更新的表大小暴增。

除了實體複制,replication slot對邏輯複制的意義更大,我們期待在可能出現邏輯複制功能的9.5版本中看到它大顯身手。

繼續閱讀