天天看點

PgSQL · 特性分析· Logical Decoding探索

logical decoding是9.4裡面的一個主要功能,是向最終實作邏輯複制邁出的一大步。簡言之,它的功能是從pg的wal日志中,讀取資料庫更新資訊,然後“翻譯”(decode)成邏輯的形式,可發送到遠端從庫做資料同步。這個功能還可以用于,dba在資料庫當機,并發生主從切換後,檢查原主庫有哪些更新當機前未同步到從庫,并手動同步來彌補丢失的(已送出)的更新。這裡我們探索一下它的使用和實作原理。

<b>使用</b>

1)首先需要将 wal_level這個配置參數設定為logical,并保證max_replication_slots至少為1。

2)建立logical replication slot。logical decoding利用了logical replication slot來擷取和decode日志。關于physical replication slot我們在上期中有詳細介紹,而logical replication slot與physical replication slot的資料結構類似。建立一個logical replication slot的指令如下:

注意,建立logical replication slot,需要指定一個輸出插件(output plugin)。這個插件要提供一些回調函數,用于格式化輸出日志。就是說,核心中的logical decoding先讀取wal日志,将其decode成一種半成品式的格式(已包含所有有效資訊,比如被更新的表名,更新類型,更新前後的資料記錄即tuple),然後交由輸出插件最終呈現給使用者。這裡我們使用了系統自帶的一個插件,即test_decoding。上面的輸出中,建立指令傳回的xlog_position的值是目前系統中,最後被寫入磁盤的日志記錄的lsn。此lsn之後的日志,都可以通過logical decoding進行解析了。

3)解析日志。

pg_logical_slot_peek_changes傳回資料中的第二行記錄了我們所做的insert操作(隻有在事務送出後,才能看到這些修改)。而我們通過pg_xlogdump可以看到原來的wal日志記錄為:

也就是說,logical decoding把這條日志,反解析成一個“table public.test: insert: col[integer]:2”字元串。其實如果對輸出插件稍作修改,可以直接解析成可執行的sql語句:“insert into public.test (col) values(2)“

那麼這是如何做到的呢?下面我們看看其中原理。

<b>原理</b>

追蹤一下pg_logical_slot_peek_changes的調用鍊,不難看到decoding的整個過程。在pg_logical_slot_get_changes_guts中,從restart_lsn(即上次的最後讀取後,剩下的事務中最先開始的事務對應的lsn)開始,先用xlogreadrecord函數(注意,會先從cache裡面讀取日志,如果cache裡面沒有,則會到磁盤中的日志段裡面讀取)擷取一個日志記錄,存入結構體xlogrecord,緊接着用logicaldecodingprocessrecord做decode。如此循環,直到讀完日志或到達指定點。

logicaldecodingprocessrecord是解析日志的關鍵。它在記憶體中維護一個哈希表(logicaldecodingcontext-&gt;reorder-&gt;by_txn),存放正在處理的事務資訊。在處理每個日志記錄時,如果遇到一個begin操作,就在哈希表中插入相應事務。而隻有在遇到commit操作的時候,才會把整個事務的所有語句解析出來(調用reorderbuffercommit)。這個過程中,它要為每個事務維護一個快照(snapshot)。每次有事務做commit都要更新一下這個快照。這樣,等到事務commit時,它的快照是最新的,可以用來通路系統表,得到如relation node id與relation名字之間的對應關系等資訊,進而完成decode。需要說明的是,logicaldecodingprocessrecord在維護快照時做了優化:因為decode過程隻需要通路系統表,是以快照中隻保留了那些更新了系統表的事務。

另外,replication slot的xmin資訊會影響系統的vacuum,使其保留仍然需要的資料版本。而snapbuildprocessrunningxacts會不斷更新replication slot中的xmin資訊,避免使vacuum停滞。

輸出decode後日志的過程,都在decodecommit調用的reorderbuffercommit函數中。在reorderbuffercommit中,調用了輸出插件的apply_change等回調函數,會将日志資訊列印成我們最終看到的字元串,這樣就完成了decode。

繼續閱讀