ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

PostgreSQL备份与恢复实战:从pg_dump到PITR完整指南

PostgreSQL备份与恢复实战:从pg_dump到PITR完整指南 做数据库运维这些年我见过太多“备份一时爽恢复火葬场”的场面了。PostgreSQL的备份方式翻来覆去就是那几套pg_dump逻辑转储、pg_basebackup物理基础备份、WAL归档配合时间点恢复再讲究一点就上流复制从库或专业备份工具。可就是这么几个看似简单的东西真到出问题的时候能在10分钟内恢复的人少之又少。这篇不是给官方文档背书而是结合我在生产环境里实操过的经验把PostgreSQL备份从选型、命令、坑点到恢复演练一次性讲透。适合刚接手PG维护的DBA、准备把备份方案升级一下的团队也适合那些“备份脚本一直在跑但从来没恢复过”的兄弟——看完你就明白备份方案到底该怎么设计。1. 备份方案设计先回答三个问题再选工具很多人一上来就问“用pg_dump还是pg_basebackup”我习惯先反问三个问题要恢复什么能接受丢多少数据恢复要多快这三个问题直接决定了备份方案的长相。1.1 三条基准线RPO、RTO与恢复粒度RPO恢复点目标回答的是“能丢多少数据”。业务方说“最多丢5分钟”那你就必须在WAL归档或流复制上做文章如果业务方说“丢一天无所谓重导一遍就行”那每天跑一次pg_dump也说得过去。RTO恢复时间目标回答的是“多久能恢复”。逻辑备份恢复一个几百GB的库可能要小时级而物理备份配合归档可以做到分钟级前提是脚本、流程都到位。除了RPO和RTO我还会加一条“恢复粒度”。误删一张表、一个schema和整个实例崩溃诉求完全不一样。误删单表时用逻辑备份最舒服可以直接把那张表捞出来实例崩溃时物理备份是兜底逻辑备份在这种场景下又慢又痛苦。所以成熟方案里通常两者共存逻辑备份管“细粒度找回”物理备份管“整体灾难恢复”。1.2 三种备份方式的选型逻辑我见过不少人把备份方式分成“逻辑备份”和“物理备份”两类但在实际工程里应该分成三档方式原理恢复粒度恢复速度典型场景逻辑备份pg_dumpSQL/自定义格式导出数据库、schema、表级慢数据量越大越明显小库、单表误删找回、跨版本迁移物理冷备直接拷贝数据目录停库后文件拷贝整个实例快但需要停服低可用性要求的测试环境、临时环境物理备份WAL归档pg_basebackupPITR基础备份持续归档WAL整个实例可恢复任意时间点较快且能精确到秒级恢复点生产环境标准配置支持误操作回滚选择逻辑很简单数据量在几十GB内、业务容忍度高的逻辑备份够用了数据量上百GB、业务不能长时间中断的直接上“物理备份WAL归档从库”至于纯物理冷备我只能说能用但前提是你能接受停库那段窗口生产环境我一般不建议。2. 逻辑备份实战pg_dump 组合拳pg_dump是PostgreSQL自带的老牌备份工具别嫌它“太基础”很多生产事故最终就是靠它救回来的。2.1 pg_dump 核心参数抄作业版裸跑一条pg_dump -d mydb mydb.sql确实能备份但这只是入门。真正实用的是这些参数pg_dump -h 127.0.0.1 -p 5432 -U backup_user \ -d mydb \ -Fc -f /backup/mydb_$(date %Y%m%d).dump-Fc太重要了。它生成的是PostgreSQL自定义格式的归档文件不是纯文本SQL。为什么推荐它因为只有自定义格式才能配合pg_restore做选择性恢复、并行恢复、排序恢复这些在“误删一张表要捞数据”的场景里都是救命能力。纯SQL文本文件虽然可以直接psql灌进去但一张表误删了根本没法定向提取。如果只想备份某几个表或某个schema用-t和-n过滤pg_dump -d mydb -n public -t orders_2024 -Fc -f /backup/orders_2024.dump注意-t和-n可以组合但表名匹配规则比较讲究支持通配符生产环境我建议先在测试库跑一遍pg_dump --schema-only确认对象集合再全量导出避免漏表。很多时候备份用户不是超级用户。建议单独建一个备份账号只授予需要的权限比如CREATE USER backup_user WITH PASSWORD xxxx; GRANT CONNECT ON DATABASE mydb TO backup_user; GRANT USAGE ON SCHEMA public TO backup_user; GRANT SELECT ON ALL TABLES IN SCHEMA public TO backup_user;如果库里有序列、函数、触发器还需要额外授权。图省事可以直接拿超级用户做备份但生产环境安全规范一般不允许我碰到过因为权限不足导致备份出来的文件不完整的情况恢复时才发现少了好几张表那真是血压飙升。2.2 pg_restore别只会 psql 一把梭备份的最终目的是恢复可我看到太多人只会psql -f backup.sql硬灌完全不碰pg_restore。自定义格式必须用pg_restore这个工具的真正价值在选择性恢复pg_restore -h 127.0.0.1 -U postgres \ -d mydb \ --clean --if-exists \ -t orders_2024 \ /backup/mydb_20240101.dump这条命令能把归档里的orders_2024表单独恢复到目标库--clean --if-exists会先删除目标表再重建避免重复执行报错。并行恢复也是它的强项pg_restore -j 4 -d newdb /backup/mydb_20240101.dump-j指定并行度能大大缩短恢复时间但前提是目标库的CPU和IO扛得住我一般从4开始而不是无脑拉高。有一点必须提醒pg_restore恢复时默认是“先结构后数据再索引”索引是最后建的。这是好事建索引很耗IO放到最后能让整体恢复更稳。但如果你恢复的是生产库恢复完成后要记得手动ANALYZE否则优化器对统计信息一无所知查询性能会明显变差。2.3 pg_dumpall 与全局对象pg_dump只导出数据库本身不导出集群级别的角色、表空间。所以一个完整的逻辑备份方案必须包含pg_dumpall --globals-onlypg_dumpall --globals-only -U postgres -f /backup/globals.sql这点极容易被忽略。我接过一个项目之前的DBA只备份业务库后来服务器迁移新环境连角色都没建数据导进去后一堆权限错误光是重建角色就耗了半天。正确做法是每天的备份任务里加上这条并在恢复时先执行globals.sql再执行数据库级恢复。2.4 逻辑备份的三个注意点第一pg_dump默认是一致性快照不需要锁表但它会对系统表、元数据有额外开销大库备份时段尽量避开业务高峰。我一般安排在凌晨低峰期并错开批处理任务。第二不要在备份命令里直接写明文密码。用.pgpass文件或PGPASSWORD环境变量但PGPASSWORD在cron里会被别的进程看到谨慎使用。我更推荐在用户目录放.pgpass127.0.0.1:5432:mydb:backup_user:密码然后chmod 600 ~/.pgpasspg_dump会自动读取。第三备份文件要带时间戳并且定期清理旧文件。我见过一个老哥备份脚本没问题但从不清理结果磁盘被备份文件塞满数据库直接只读了讽刺得很。3. 物理备份与PITRpg_basebackup WAL归档如果你的业务已经跑到上百GB还指望每天pg_dump全量备份那恢复时间会让人抓狂。这时候必须用物理备份而且要和WAL归档配合做成时间点恢复PITR。3.1 没有WAL归档basebackup只是半个方案先理清概念。pg_basebackup做的是基础备份它拷贝的是某个一致性检查点时刻的数据目录。如果只有这一份基础备份你只能恢复到“备份完成那一刻”之后的数据全丢。要实现“恢复到任意时间点”必须配合WAL归档。WAL就是PostgreSQL的预写日志记录了所有数据变更。基础备份加上从备份点到目标时间的WAL归档就是完整的时间点恢复能力。用生活类比基础备份是一张照片WAL是一段录像PITR能让你把录像倒到任意一帧。开启归档需要调整postgresql.confwal_level replica archive_mode on archive_command test ! -f /backup/archive/%f cp %p /backup/archive/%fwal_level至少是replica如果要用逻辑复制则设为logical。archive_command里的%p是源WAL路径%f是文件名。前面加test ! -f是为了避免归档重名覆盖这也是官方文档的建议。改完参数要重启实例或者执行SELECT pg_reload_conf()。这里有个常见误解archive_mode和archive_command改了之后wal_level必须重启才能生效不是reload就行的具体用pg_ctl restart或重启服务。我用的是本地目录归档生产环境更建议归档到独立的备份服务器或对象存储避免和数据库同盘故障。归档目录要做定期清理保留周期取决于你的恢复窗口和磁盘成本我一般保留最近7天WAL加上每日全量备份总共能覆盖近几天的任意时间点。3.2 pg_basebackup 实测步骤先确认主库给了备份用户合适的权限通常需要REPLICATION权限CREATE USER backup_user WITH REPLICATION PASSWORD xxxx;然后在从库或应用服务器上执行pg_basebackup -h 主库IP -p 5432 -U backup_user \ -D /backup/base/$(date %Y%m%d) \ -Fp -Xs -P -R参数拆解-Fp输出为普通文件格式比-Ft的tar格式更适合直接当数据目录用。-Xs流式复制WAL不在本地生成pg_wal临时文件速度更快生产环境用这个。-P显示进度。-R自动生成standby.signal这在搭建从库时特别有用后面会讲。执行完检查备份目录是否完整尤其看看backup_label和tablespace_map是否存在这两个文件是基础备份的身份标识。备份完成后我建议在那个备份目录上再跑一次校验/usr/pgsql-16/bin/pg_controldata /backup/base/20240101如果能正常输出说明备份目录的基本结构没问题。3.3 一次完整的时间点恢复演练纸上谈兵没意思直接走一遍PITR恢复流程。前提是你有一份基础备份/backup/base/20240101以及从那一刻开始到目标时间点的完整WAL归档归档目录在/backup/archive/。第一步把基础备份放到新数据目录比如/var/lib/pgsql/16/data。注意目录权限必须是postgres用户可读写否则启动会报错。cp -a /backup/base/20240101/* /var/lib/pgsql/16/data/ chown -R postgres:postgres /var/lib/pgsql/16/data/第二步创建恢复信号文件。PostgreSQL 12及以后用recovery.signal12之前在数据目录放recovery.conftouch /var/lib/pgsql/16/data/recovery.signal第三步配置恢复目标。在postgresql.conf或postgresql.auto.conf里加restore_command cp /backup/archive/%f %p recovery_target_time 2024-01-01 03:30:00 recovery_target_inclusive true recovery_target_action pauserecovery_target_time写你要回滚到的时刻比如业务误删数据发生前的时间点。pause的意思是达到目标后暂停恢复先别急着写库等人工确认数据没问题。第四步启动实例并观察日志pg_ctl -D /var/lib/pgsql/16/data start启动后实例会进入恢复模式持续应用WAL。查看日志确认“recovery stopping before commit of transaction”之类的信息说明已经到了目标点。此时数据库是只读状态可以查询验证数据。第五步确认一切正常后结束恢复并提升为主库SELECT pg_promote();执行完数据库变成可读写。如果检查发现恢复目标不对别急着promote停下来重新调整参数再来一次。这个流程我第一次跑的时候花了近两个小时才搞清楚原因是restore_command里%p的路径没匹配到归档目录结构一直报“could not open file”。后来才明白%p是PostgreSQL期望的目标路径%f才是归档文件名两个变量对应关系不能搞反。3.4 初始化参数自查避免新装环境挖坑很多“安装教程”不会讲清楚备份相关参数等你真正要做PITR时才发现archive_mode还是offwal_level还是minimal那就只能推倒重来。新装PostgreSQL之后我建议第一时间检查这几项参数推荐值说明wal_levelreplica默认就是replica但别手动降到minimalarchive_modeon默认off必须改archive_command归档命令提前配好并测试命令可执行max_wal_senders10左右给从库和pg_basebackup预留连接hot_standbyon从库上允许只读查询用于备份和分流这几个参数里wal_level修改需要重启max_wal_senders也需要重启所以最好在数据库初始化阶段就定好别上线后再折腾。3.5 记住3-2-1原则无论用什么工具备份的基本盘是3-2-1至少3份数据拷贝2种不同的存储介质1份存到异地或至少独立于生产环境的地方。我见过不少团队备份文件和数据库同在一块物理盘上硬盘一坏备份全没这是最典型的“备份了个寂寞”。哪怕只是每周把备份文件同步到另一台机器也能规避很大一部分风险。4. 自动化备份与从库把“备份”变成系统能力手动备份能跑通但生产环境不能依赖人肉。真正的备份方案应该是自动化的最好还能利用从库来分摊压力。4.1 流复制从库备份的天然帮手PostgreSQL的流复制从库不只是高可用组件它同时也是备份的好帮手。在从库上跑逻辑备份不影响主库性能在从库上做物理备份也能得到一份一致性拷贝因为从库本身就在持续应用WAL。搭建从库的经典流程第一步主库配置wal_level replica max_wal_senders 10 hot_standby on同时在pg_hba.conf允许备份用户从从库IP发起复制连接host replication backup_user 192.168.1.0/24 md5第二步在从库机器上执行pg_basebackup -h 主库IP -U backup_user \ -D /var/lib/pgsql/16/data \ -Fp -Xs -P -R-R会自动生成standby.signal和主库连接信息primary_conninfo重启从库实例后它就是流复制备库了。第三步验证同步状态SELECT client_addr, state, sync_state, replay_lsn FROM pg_stat_replication;看到statestreaming就是正常的。有了从库备份任务可以这么安排主库负责生产业务和WAL归档。从库负责日常pg_dump逻辑备份避开主库IO消耗。定期在从库上做一次pg_basebackup物理备份作为灾难恢复兜底。要注意从库的WAL保留问题。如果从库跟不上主库的WAL可能被清理导致从库中断这时能从pg_stat_replication看到replay_lsn落后太多。解决方法是定期检查延迟并给从库设置合理的wal_keep_size或使用复制槽。4.2 备份脚本模板把命令变成服务我这边分享一个生产环境里用过的逻辑备份脚本核心是一个bash脚本加crontab简单可靠。#!/usr/bin/env bash set -euo pipefail BACKUP_DIR/data/backups/pg_dump KEEP_DAYS7 DB_HOST127.0.0.1 DB_PORT5432 DB_NAMEmydb LOG_FILE/var/log/pg_backup.log mkdir -p ${BACKUP_DIR} TIMESTAMP$(date %Y%m%d_%H%M%S) OUT_FILE${BACKUP_DIR}/${DB_NAME}_${TIMESTAMP}.dump if pg_dump -h ${DB_HOST} -p ${DB_PORT} -U backup_user \ -d ${DB_NAME} -Fc -f ${OUT_FILE}; then echo $(date %F %T) backup ok ${OUT_FILE} ${LOG_FILE} else echo $(date %F %T) backup failed ${LOG_FILE} exit 1 fi # 校验归档可读性 pg_restore -l ${OUT_FILE} /dev/null 21 || { echo $(date %F %T) archive corrupt: ${OUT_FILE} ${LOG_FILE} exit 1 } # 清理旧备份 find ${BACKUP_DIR} -name *.dump -mtime ${KEEP_DAYS} -delete脚本里两点很关键一是set -euo pipefail。没有这个pg_dump失败时脚本可能继续往下跑给你生成一个空文件备份日志还显示成功这是最阴的坑。二是pg_restore -l校验归档文件能否被正确读取。这能提前发现文件损坏而不是等到恢复时才炸。crontab配置30 2 * * * /usr/local/bin/pg_backup.sh /var/log/pg_backup_cron.log 21这里有个细节cron环境变量和交互式终端不同脚本里的命令建议用绝对路径PATH也在脚本开头重新设置否则很可能出现手动执行正常、cron执行报错的玄学问题。4.3 每季度一次的恢复演练说句不中听的很多团队的备份方案从上线起就没真正恢复过。文件每天都在增长日志显示success但没人知道恢复起来需要多久、会不会报错。我从一个事故里学到教训。当时磁盘坏道导致备份文件损坏等要恢复时才发现一堆文件打不开只能连夜从磁盘镜像里捞数据。从那以后我强制规定每季度至少做一次恢复演练把最新备份恢复到一台临时实例跑几个核心查询验证数据一致性和恢复时长。演练结果要记录超过RTO目标就要调整方案。这也是为什么我在脚本里坚持做pg_restore -l校验的原因它虽然不能100%证明数据能恢复但至少能证明归档文件的基本完整性。5. 企业级工具与跨库经验参考如果你管理的库不止一两个而是几十几百个手动脚本可能已经不够用了。这时候可以看看专业的备份工具它们解决的痛点是全量、增量、压缩、保留策略、远程备份这些东西靠手搓都可以但维护成本很高。5.1 pgBackRest更省心的企业级选择pgBackRest是PostgreSQL生态里很成熟的开源备份工具它最吸引我的是增量差量备份和存储端压缩加密。配置文件/etc/pgbackrest.conf大致长这样[global] repo1-path/backup/pgbackrest repo1-retention-full7 process-max4 log-level-consolewarn [mycluster] pg1-path/var/lib/pgsql/16/data创建备份集并做第一次全量备份pgbackrest --stanzamycluster --typefull backup之后的增量备份pgbackrest --stanzamycluster --typeincr backup恢复命令pgbackrest --stanzamycluster --typetime --target2024-01-01 03:30:00 restore用pgBackRest最大的好处是它把基础备份和WAL归档管理统一起来不用自己写归档命令和清理逻辑。它还天然支持并行、压缩和断点续传对跨机房、跨地域备份场景很有用。5.2 Barman 与其它组合Barman是另一款很有名的PG备份管理工具命令风格更偏向“服务化”安装后通过barman backup、barman recover来操作。和pgBackRest相比Barman在远程备份和保留策略上做得也很细致但配置相对更重一些。选择哪个工具我个人的经验是单机构、简单场景pg_basebackup crontab WAL归档完全够用管理多套PG实例、需要标准化备份恢复流程的直接上pgBackRest已经有Barman使用经验的团队继续用Barman也没问题。工具本身不是决定因素关键是恢复流程被反复演练过。5.3 从 XtraBackup 经验看 PG 的备份生态如果你用过MySQL大概率对XtraBackup很熟悉。它是一个物理热备工具通过复制InnoDB数据文件和支持增量备份来工作。换成PostgreSQL生态很多人会下意识问“PG有没有对应的XtraBackup”答案是没有完全对等的工具但pg_basebackupWAL归档已经覆盖了XtraBackup的核心能力。XtraBackup的思路是文件级别的物理备份加redo日志应用PG的pg_basebackup是数据目录一致性快照加WAL日志应用本质上都是“基础备份连续日志”的组合。区别在于XtraBackup可以自己控制增量备份而PG通常靠WAL归档的连续滚动配合pgBackRest再做增量管理所以跨库经验可以平移但具体命令和恢复模型必须重新学。我在一线带过不少MySQL转PG的DBA最常犯的错是拿MySQL的思维方式去套PG比如习惯性每天做全量物理备份、觉得归档没用。实际上PG的WAL归档能力很强基础备份没必要天天做归档连续性和完整性才是核心。5.4 Windows 与版本选型的小建议PostgreSQL在Windows下做备份逻辑和Linux完全一样只有几个地方容易踩坑。第一pg_dump、pg_restore、pg_basebackup这些命令在安装目录的bin子目录下很多人装了PostgreSQL却找不到命令是因为没加入PATH环境变量。直接用绝对路径也行比如C:\Program Files\PostgreSQL\16\bin\pg_dump.exe。第二Windows下路径里有空格命令要加引号我自己就吃过这个亏 C:\Program Files\PostgreSQL\16\bin\pg_dump.exe -h localhost -U postgres -Fc -f D:\backup\mydb.dump mydb第三Windows服务启动失败是常见问题。安装完PostgreSQL服务如果连不上先看服务管理器和pg_log目录下的日志很多是数据目录权限不对。这个问题和备份无关但备份脚本跑不起来往往就是从服务不正常开始的。版本选型上总有朋友问“PostgreSQL下载哪个版本”“有没有16便携版”。我的建议是新项目直接上最新的稳定大版本比如当前用16或17生产环境旧版本如果跑得稳不要为了追新随便升级升级前先用逻辑备份pg_upgrade做演练便携版适合本地测试不适合生产因为它通常跳过了一些系统服务和参数默认配置做备份恢复时行为可能和正常安装版不一致。选版本的重点不是“最新”而是“你的团队对它足够熟悉且和驱动、周边工具兼容”备份方案也一样关键是稳定可复用不是花哨。6. 常踩的坑和备份健康检查清单分享一些我在实际运维中遇到的真实报错和解决思路Android和Windows上我都碰到过整理成速查表方便你遇到问题时快速定位。6.1 高频报错排查表错误现象原因解决方案pg_dump: error: permission denied for schema备份用户缺少schema权限授权USAGE/SELECT或改用超级用户备份pg_basebackup: could not receive data from server: ERROR: requested WAL segment ... has already been removed主库WAL已被清理基础备份期间归档不及时加大wal_keep_size或配置复制槽确保备份期间WAL完整archive_command返回非0日志一直报archive_command failed归档目录不存在、权限不足或命令语法错误手动执行归档命令排查确认目录存在且可写恢复时报could not open file恢复卡住restore_command里%p和%f路径不对找不到归档WAL检查restore_command写法确认归档文件确实在指定目录pg_restore: error: relation does not exist恢复时目标表不存在或对象依赖顺序不对用--clean --if-exists必要时先恢复结构再恢复数据从库启动后没有进入standby模式没创建standby.signal或primary_conninfo没配确认数据目录中有standby.signal检查主从连接配置Windows下执行pg_dump提示不是内部或外部命令bin目录未加入PATH使用完整路径或手动添加环境变量6.2 三个独家避坑技巧第一备份校验不能少。每次备份完至少做一次归档可读性校验比如pg_restore -l能正常列出内容或对SQL压缩包执行gzip -t。我见过太多备份文件“表面完好”实际已损坏的情况尤其磁盘老化或异常断电之后。校验成本很低但能在关键时刻救你一命。第二备份恢复前先用pg_controldata检查数据目录状态。它能显示当前数据目录的系统标识符和最近一次检查点位置如果状态异常就不要硬着头皮启动。这个工具名称常被忽略但它在排查物理备份时比任何日志都直接。第三备份脚本加上微信/邮件通知。不只是失败要通知成功也要有记录因为“失败时没报错”往往比“明确失败”更可怕。我习惯在所有备份任务里做状态标记失败时进程退出码非0调度系统盯着退出码即可同时保留最近一次成功备份的时间戳如果超过24小时没更新就要主动排查。最后说一个我自己的实操习惯每做一次备份方案升级我都会在新环境里完整跑一遍从备份到恢复的流程把恢复时需要的所有命令、路径、用户权限整理成一份文档放到团队共享。因为备份方案的真正价值不在“备份文件存在”而在于“灾难发生时你还能不能按照预案快速站起来”。这套流程我用了很多年每次救场靠的都是平时扎扎实实的演练而不是临时翻文档。
返回列表