天天看點

PostgreSQL flashback(閃回) 功能實作與介紹

PostgreSQL , 髒讀 , 事務 , flashback , 閃回 , drop , truncate , dml

閃回的需求往往是救命的需求,因為通常情況下資料庫正常運作是不需要閃回的,往往是出現了誤操作,被攻擊,被注入後,資料庫的資料被删除或惡意纂改并且纂改的事務已送出,也就是說纂改已經被持久化了。

這種情況下需要閃回來救命,回到被破壞前的狀态。

DML閃回和DDL閃回。

DML閃回指對INSET, UPDATE, DELETE操作的閃回。DDL閃回指DROP, TRUNCATE操作的閃回。

1、實體回退,相當于使用實體備份和歸檔進行時間點恢複,全庫恢複到誤操作前的狀态。

(可以建立一個庫用于恢複,恢複到目标時間點,恢複後,将誤操作前的資料導出來,再導入線上資料庫。)

2、在目前庫回退,在目前庫,将誤操作影響的資料找出來。

1、實體回退,PG核心已支援時間點恢複,隻要有誤操作前的全量備份和所有歸檔即可。

2、目前庫回退,使用HOOK,可以實作DROP和TRUNCATE操作的資源回收筒功能。

3、使用延遲垃圾回收、髒讀、行頭事務号、事務送出日志,可以實作DML操作的閃回。

以前寫的一些閃回方案:

<a href="https://github.com/digoal/blog/blob/master/201408/20140828_01.md">《PostgreSQL 閃回 - flash back query emulate by trigger》</a>

<a href="https://github.com/digoal/blog/blob/master/201504/20150429_01.md">《PostgreSQL Oracle 相容性之 - 事件觸發器實作類似Oracle的資源回收筒功能》</a>

<a href="https://github.com/digoal/blog/blob/master/201404/20140403_01.md">《PostgreSQL 資源回收筒功能 - 基于HOOK的recycle bin pgtrashcan》</a>

<a href="https://github.com/digoal/blog/blob/master/201608/20160823_04.md">《PostgreSQL 最佳實踐 - 任意時間點恢複源碼分析》</a>

<a href="https://github.com/digoal/blog/blob/master/201608/20160823_03.md">《PostgreSQL 最佳實踐 - 線上增量備份與任意時間點恢複》</a>

<a href="https://github.com/digoal/blog/blob/master/201512/20151220_06.md">《阿裡雲ApsaraDB RDS for PostgreSQL 最佳實踐 - 6 任意時間點恢複》</a>

<a href="https://github.com/digoal/blog/blob/master/201302/20130205_01.md">《PostgreSQL PITR THREE recovery target MODE: name,xid,time USE CASE - 2》</a>

<a href="https://github.com/digoal/blog/blob/master/201302/20130204_01.md">《PostgreSQL PITR THREE recovery target MODE: name,xid,time USE CASE - 1》</a>

本文将詳細介紹“使用延遲垃圾回收、髒讀、行頭事務号、事務送出日志,實作DML操作的閃回。”的方法。

1、延遲VACUUM,確定誤操作的資料還沒有被垃圾回收。

2、記錄未被freeze,確定無操作的資料,以及後面送出的事務号沒有被freeze(抹去)。

3、開啟事務送出時間跟蹤,確定可以從xid得到事務結束的時間。(開啟事務結束時間跟蹤後,會開辟一塊共享記憶體區存儲這個資訊。)。

4、事務送出時間跟蹤未抹去。可以加大BUFFER來增加可跟蹤的事務數。(重新開機資料庫不影響,有持久化已有的跟蹤記錄)

1、将xid轉換為txid的函數,見本文末尾。(因為記錄的行頭部,存儲的是XID,而不是TXID,而查詢事務送出狀态,用的是TXID,是以需要轉換一下。)

2、髒讀插件pg_dirtyread(用于讀取髒頁)

<a href="https://github.com/ChristophBerg/pg_dirtyread">https://github.com/ChristophBerg/pg_dirtyread</a>

3、根據txid查詢事務送出狀态

4、根據xid查詢事務送出時間

5、使用髒讀插件,将需要flashback的資料(連同頭資訊xmin,xmax)寫入臨時表,根據以上兩種方法生成如下字段:

寫入事務送出狀态、事務送出時間。(xmin)

删除事務送出狀态、事務送出時間。(xmax)

例如

1、根據pg_xlogdump找到精确的誤操作xid,以及事務送出對應的時間戳。(或者使用使用者提供的時間戳,大概的誤操作前的時間。)

參考:

<a href="https://github.com/digoal/blog/blob/master/201512/20151210_01.md">《PostgreSQL 使用pg_xlogdump找到誤操作事務号》</a>

2、回退到過去的某個時間點(采用基于臨時表的VIEW來展現) (根據事務送出順序,逆序,逐個事務排除,逐個事務回退。)

例子

3、找出被誤操作事務 删除、更新、插入 的記錄(OLD ROW(被删除、更新前的), NEW ROW(更新後的、誤插入的))。

flashback的前提,前面已經講了,但是可能帶來一些負面影響。

1、由于設定了vacuum_defer_cleanup_age,是以每次都會被觸發VACUUM,掃描表,但是又有一些垃圾不能被回收;

2、可能導緻表膨脹。

1、提高autovacuum_naptime,進而降低頻繁掃描的機率。

2、膨脹可能無法解決。建議修改核心,實作可以僅針對重要的表設定 vacuum_defer_cleanup_age。進而避免全庫膨脹。

Name

Return Type

Description

txid_status(bigint)

txid_status

report the status of the given transaction: committed, aborted, in progress, or null if the transaction ID is too old

pg_xact_commit_timestamp(xid)

timestamp with time zone

get commit timestamp of a transaction

<a href="https://www.postgresql.org/docs/10/static/functions-info.html">https://www.postgresql.org/docs/10/static/functions-info.html</a>

開啟事務結束時間跟蹤後,會開辟一塊共享記憶體,跟蹤事務結束時間。

xid是32位的整型,會被FREEZE,循環使用,xmin,xmax都是XID類型。通過xid可以得到事務結束時間。

txid是64位整型,不會被循環使用,通過epoch可以從xid轉換為txid。通過txid可以得到事務的送出狀态。

我們可以自定義一個函數,将xid轉換為txid。

編譯

拷貝到資料庫軟體LIB目錄

建立函數

測試

為什麼會有EPOCH呢?因為XID是循環使用的,每一個循環都會使得EPOCH自增1。進而使得TXID可以持續增長。

<a href="https://github.com/digoal/blog/blob/master/201109/20110930_03.md">《Use pg_resetxlog simulate tuple disappear within PostgreSQL》</a>

<a href="https://github.com/digoal/blog/blob/master/201109/20110930_01.md">《PostgreSQL xid(int4) to txid(int8)》</a>

誤操作後,如果又發生了DDL,例如新增字段,修改字段等。使用本文提供的閃回方法,有沒有問題?

測一下就知道了,不會有問題。

本文介紹了原地閃回的一種方法:

1、新增髒讀功能、新增表級vacuum_defer_cleanup_age(避免全庫膨脹)功能、開啟track_commit_timestamp。可以實作dml flashback(閃回)。

2、增加資源回收筒(通過HOOK)功能,可以實作DDL flashback。

全庫閃回,可以參考PostgreSQL的PITR的功能。

原地閃回的另一種方法:利用redo的undo内容進行閃回,這種方法可以避免膨脹的問題,回退到什麼時間點,與保留的REDO檔案數有關。使用redo來閃回也會引入一個問題,UNDO需要記錄更多的内容,導緻REDO檔案内容變多。是以建議也是設定表級redo的UNDO内容。