備份與還原時間窗¶
一個目標的歷史是一條鏈:依排程進行的完整備份,以及兩次完整備份之間連續不斷的變更串流(Postgres WAL、MySQL binlog)。 凡是被某個完整備份及其後續變更覆蓋的每一秒,都可以還原。這段跨度就是時間點還原時間窗。
gantt
dateFormat YYYY-MM-DD HH:mm
axisFormat %a %d
section Full backups
daily full :milestone, 2026-10-01 02:07, 0d
daily full :milestone, 2026-10-02 02:07, 0d
daily full :milestone, 2026-10-03 02:07, 0d
daily full :milestone, 2026-10-04 02:07, 0d
section Change stream
WAL / binlog segments :active, 2026-10-01 02:07, 2026-10-04 18:00
完整備份¶
| 引擎 | 模式 | 工具 | 還原 |
|---|---|---|---|
| Postgres 15–17 | physical(預設) |
透過複製協定執行 pg_basebackup -Ft -X fetch |
基礎備份 + WAL 重放,精確到秒 |
| Postgres 15–17 | logical |
pg_dump -Fc,每個資料庫一個物件;支援排除表和選擇性匯出 |
pg_restore,存在實體鏈時再透過它重放 WAL |
| MySQL 8.4 | logical |
mysqldump --single-transaction --source-data=2(例程、事件、觸發器) |
載入 + binlog 重放,精確到秒 |
每個備份都會記錄一份清單:資料庫、表、欄、索引、列數估計和大小,以及存在遷移表時的遷移版本。
控制台和 tenax diff 比較的就是這些內容。
變更串流¶
每個啟用了時間點還原的目標都有一個串流傳輸程式:
- Postgres:Tenax 自己的 WAL 接收器,基於實體複製槽(ADR 0005)。 複製槽保證串流傳輸程式重啟期間不遺失任何 WAL。
- MySQL:從上一次完整備份的 binlog 位置開始執行
mysqlbinlog --read-from-remote-server --raw --stop-never。
每個區段寫完即上傳,仍在寫入的區段每隔 partialInterval(預設 60 秒)上傳一次,所以最後一分鐘同樣可以還原。
每個區段都附帶一份摘要:每張表的提交、插入、更新和刪除次數、DDL,以及選用的遮罩列預覽。
如果串流中斷(複製槽遺失、binlog 被清除),Tenax 會把這條鏈標記為斷裂,發出警示(TenaxStreamChainBroken),
啟動一條新鏈並立即進行一次完整備份(原因為 chain-restart)。舊時間窗在斷點之前的部分仍然可以還原。
保留¶
完整備份依層級保留;變更區段只保留到時間窗所需的時間範圍;其餘一切由垃圾回收清除,但已固定的備份絕不會被刪除。
| 預設 | 時間窗 | 每日 | 每週 | 每月 | 每年 |
|---|---|---|---|---|---|
prod |
7 天 | 14 | 8 | 12 | 5 |
dev |
3 天 | 7 | 4 | 3 | 0 |
critical |
14 天 | 30 | 12 | 24 | 7 |
一天中第一個提交的完整備份就是當天的每日備份;一個 ISO 週中的第一個就是當週的每週備份,依此類推。
一個備份可以同時屬於多個層級,不會產生任何複製。固定(tenax pin <id> --reason …)可以讓備份不受層級約束地保留下來。
GC 在刪除前會先把計畫寫入儲存貯體,先刪 manifest 再刪資料,並在一天後清理孤立的和未完成的上傳。
儲存格式¶
<store prefix>/<org>/<project>/<namespace>/<target>/
full/<backup-id>/manifest.json # written last: the commit marker
full/<backup-id>/data/<database>.zst.age # dumps or the base backup tar
full/<backup-id>/inventory.json.zst.age # also readable with the catalog key
full/<backup-id>/tags.json # tiers, pins, verification results
stream/…/<segment>.zst.age # WAL / binlog segments and their summaries
stream/position.json # the streamer's confirmed position
- 壓縮:依策略設定的級別執行 zstd 壓縮,只壓縮一次,在加密之前進行;預設開啟
(ADR 0013)。
compression.algorithm: none則依原樣儲存資料。 - 加密:每個物件都在用戶端用 age 加密給儲存的接收者。儲存貯體永遠看不到明文。
- 完整性:每個物件的大小和 SHA-256 都記錄在 manifest 中,每次讀取時都會驗證。
- 提交:備份只有在寫入
manifest.json之後才算存在。在此之前發生崩潰只會留下由 GC 清除的孤立物件, 絕不會出現一個看起來完整的備份。
負載控制¶
備份從不盲目地與正式環境的業務爭搶資源:有依主機、依目標和全域的並發槽、頻寬限制、優先順序、排程時間抖動,
以及一個負載保護機制,在伺服器活躍連線過多、複製延遲過大或存在長時間執行的交易時延後完整備份。
所有這些都在 BackupPolicy 中設定。