寫在前面
備份通過是否停機分為熱備和冷備,可以分為邏輯備份和物理備份,熱備又分為全量備份和增量備份。
恢復可以分為完全恢復、指定時間點恢復(PITR)。完全恢復是指恢復到數據庫損壞的那個時間點,指定之間點恢復
邏輯備份是備份某個時間點的數據,通過MVCC保證數據的一致性,恢復的時候只能恢復到備份的時間點,不可以應用日志進行前滾,通常處理時間相對物理備份會慢,MogDB通過gs_dump或者gs_dumpall來實現邏輯備份,通過gs_restore實現數據恢復。
物理備份是備份物理文件和歸檔日志,因此恢復的時候可以先還原物理文件,然后在應用歸檔日志,可以恢復到任意時間點,如果redo日志沒有丟失,可以恢復到數據庫宕機前,處理時間相對邏輯備份會高效的多。
物理備份和邏輯備份應用場景在現實生活中都比較多,相對而言描述以下使用場景:
物理備份:主要是運維人員為了保證數據庫損壞后數據少丟失,數據庫體量大的盡量使用物理備份,整庫的備份和恢復;
邏輯備份:開發人員應用較多,數據庫體量不大,只備份恢復幾個表幾個schema可以考慮使用邏輯備份恢復。
此次筆記的目的是測試物理備份和恢復。
備份
前提條件
備份命令中如果指定--xlog-method=fetch會在備份最后的階段備份wal日志,避免在備份過程中wal預寫日志文件被輪轉覆蓋,需要設置wal_keep_segments參數大一些,這樣就可以保留更多的wal預寫日志文件,保證在備份期間不會覆蓋重用原有的wal文件。也可以開啟歸檔日志,這樣在覆蓋重用之前,被覆蓋的文件會被歸檔。
[omm@pkt_mogdb1 gs_guc]$ gs_guc reload -N all -I all -c "archive_mode=on" |
備份命令中如果指定--xlog-method=stream時,會在備份過程中對wal日志進行備份,是通過wal日志流傳輸控制,如果是一主N從的物理架構,需要保證有空閑的線程用來備份時傳輸wal日志到備份文件,因此建議max_wal_senders參數的數量至少為N+1。
歸檔和參數的設置主要請參考《日志管理》的筆記。
備份數據庫
gs_basebackup -D /opt/mogdb/backup -h 10.80.9.249 -p 26000 |
沒有指定xlog-method,默認是stream。
備份成功以后打印出如下日志。
模擬數據文件丟失
查看備份路徑下的文件內容:
查看源數據庫data目錄下的內容
可以發現備份就是把源文件下的內容備份到了另外一個目錄,因此恢復的時候直接復制文件到data目錄即可,先刪除數據庫文件模擬數據丟失
數據文件都是放在data目錄下的base目錄下面,刪除base目錄及內容
[omm@pkt_mogdb1 data]$ pwd /opt/mogdb/data/data [omm@pkt_mogdb1 data]$ rm -rf base/ |
查詢數據庫
select count(*) from db_mysql.dump_tables_mysql dtm left join db_mysql.dump_tables_mysql dtm1 on dtm.tablename =dtm1.tablename; |
提示錯誤:
SQL 錯誤 [11457] [3D000]: [10.80.9.150:56205/10.80.9.249:26000] FATAL: database "db_mogdb" does not exist 詳細:The database subdirectory "base/16388" is missing. |
恢復數據庫
- 停止數據庫
[omm@pkt_mogdb1 data]$ gs_om -t stop |
2. 拷貝備份文件中的目錄到數據庫data目錄
[omm@pkt_mogdb1 backup]$ cp -r base/ /opt/mogdb/data/data/ |
3. 啟動數據庫
[omm@pkt_mogdb1 data]$ gs_om -t start |
select count(*) from db_mysql.dump_tables_mysql dtm left join db_mysql.dump_tables_mysql dtm1 on dtm.tablename =dtm1.tablename; |
能夠正常查詢,數據已經恢復
PITR恢復
通過測試發現,上面的測試只是恢復到了備份的時間點,并沒有進行前滾。因此不能恢復到出故障的時間點,依舊會丟失數據,通過PITR的方式可以恢復到物理備份數據之后的某一時間點。如果存在備機的環境,備機需要進行全量build與主全量同步,因為主庫進行了PITR恢復,導致主從數據已經不一致,注意:一定要開啟歸檔日志,并且指定歸檔日志路徑。
測試步驟:
- 全量備份數據庫
gs_basebackup -D /opt/mogdb/backup -h 10.80.9.249 -p 26000 |
[omm@pkt_mogdb1 ~]$ gsql -U zkh -W Zkh12345678 -d db_mogdb db_mogdb=>create table test_1 (id varchar(2),name varchar(50),saler decimal(10,2),dept_no varchar(2)); db_mogdb=> insert into test_1 values ('2','zs',6000,'10'); db_mogdb=>insert into test_1 values ('3','ls',7000,'10'); db_mogdb=>insert into test_1 values ('4','wemz',8000,'10'); db_mogdb=>insert into test_1 values ('5','lucy',9000,'10'); db_mogdb=>insert into test_1 values ('6','lili',10000,'10'); db_mogdb=>insert into test_1 values ('7','ww',50000,'10'); db_mogdb=>insert into test_1 values ('8','tq',10000,'10'); db_mogdb=> alter table test_1 add primary key (id); |
表test_1是備份后創建的,備份文件中是不存在的
手動切換幾次日志
select pg_switch_xlog(); select pg_switch_xlog(); select pg_switch_xlog(); |
記錄一下當前時間2022-08-17 09:27:17
執行誤操作,把所有的人工資都修改成了10000,如果當時發現可以使用閃回功能來恢復數據,此處只是為了測試,把數據恢復到update之前。
update test_1 set saler=10000: |
gs_om -t stop |
手動備份一下目標數據庫data目錄,以防出現意外情況
[omm@pkt_mogdb1 data]$ tar -zcvf data.tar.gz data/ |
替換目標數據庫的data目錄
cp -r * /opt/mogdb/data/data/ |
[omm@pkt_mogdb1 pg_xlog]$ pwd /opt/mogdb/data/data/pg_xlog [omm@pkt_mogdb1 pg_xlog]$ rm -rf * |
cp /opt/mogdb/data/archivelog/* /opt/mogdb/data/data/pg_xlog/ |
[omm@pkt_mogdb1 data]$ cat recovery.conf #restore_command 指定一個shell命令從歸檔日志路徑中把歸檔日志復制到pg_xlog中由于我已經手動復制可以不進行配置此項 #restore_command = 'cp /opt/mogdb/data/archivelog/* /opt/mogdb/data/data/pg_xlog/ ' #archive_cleanup_command 指定一個shell命令,每次重啟數據庫都會清理備庫已經不需要的配置文件,我這里不進行清理 #archive_cleanup_command = 'pg_archivecleanup /mnt/server/archivedir %r' #recovery_target_time 恢復到指定時間點的時間戳 recovery_target_time='2022-08-17 09:27:17' #recovery_target_inclusive 參數表明到達恢復時間點后是否還繼續,這里不在繼續恢復 recovery_target_inclusive=false |
[omm@pkt_mogdb1 data]$ gs_om -t start |
數據庫成功,因為主庫已經進行了PITR恢復,所以主備數據是不一致的,備庫需要重新rebuild,
登錄主庫查看數據是否恢復到我們想要的數據
可以看到數據庫已經恢復到我們想要的時間節點。
8. 重建從庫
登錄從庫,切換到omm用戶
[omm@pkt_mogdb2 ~]$ gs_ctl build -b full |
重新查看主從狀態
主從狀態已經正常。
9. 若已經恢復到預期狀態,通過pg_xlog_replay_resume()指令使主節點對外提供服務。
Call pg_xlog_replay_resume() |




