Skip to content
VDS · 数据库

数据库备份

如何定时执行 MySQL 和 PostgreSQL 转储、将其移出机器,并验证备份确实可以恢复。

更新于 2026年8月23日

VPS 不提供自动的机器备份或状态快照:任何您没有自行复制的内容都只存在于单一实例中。数据库转储是您首先应该设置的东西,因为数据没有其他地方可以恢复,而网站可以从头重新部署。

手动转储

PostgreSQL:

pg_dump -Fc -U shop -h 127.0.0.1 shop > /srv/backups/shop-$(date +%F).dump
pg_dumpall -U postgres --roles-only > /srv/backups/roles.sql

MySQL 和 MariaDB:

mysqldump --single-transaction --quick --routines --events \
  -u root shop | gzip > /srv/backups/shop-$(date +%F).sql.gz

--single-transaction 可以在不锁定表的情况下获取一致的 InnoDB 转储——没有它,您的网站会在导出期间卡住。PostgreSQL 的 -Fc 格式经过压缩,并允许您恢复单个表。

不要把密码写在命令行上:创建权限为 600 的 ~/.pgpass 或 ~/.my.cnf。

定时执行

mkdir -p /srv/backups && chmod 700 /srv/backups
crontab -e
20 3 * * * /usr/local/bin/db-backup.sh >> /var/log/db-backup.log 2>&1

/usr/local/bin/db-backup.sh 脚本:

#!/bin/bash
set -euo pipefail
DEST=/srv/backups
STAMP=$(date +%F-%H%M)
pg_dump -Fc -U shop -h 127.0.0.1 shop > "$DEST/shop-$STAMP.dump.tmp"
mv "$DEST/shop-$STAMP.dump.tmp" "$DEST/shop-$STAMP.dump"
find "$DEST" -name '*.dump' -mtime +14 -delete

先写入临时文件再重命名很重要:如果转储被中断,您不会把截断的文件误认为是完整的备份。set -euo pipefail 会在第一个错误处停止脚本,而不是静默地继续执行。

将备份移出机器

同一块磁盘上的副本无法让您免于迁移失误、勒索软件,或在“Access”标签页重装操作系统。请将转储发送到异地:

rsync -az --delete /srv/backups/ backup@other-host:/store/vds/

或者通过 rclone 发送到任何兼容 S3 的存储。请记住最高 500 Mbit/s 的带宽以及每档的磁盘写入限制:每核心 60 MB/s。

恢复检查

从未恢复过的备份不算备份。

createdb -U postgres shop_check
pg_restore -U postgres -d shop_check /srv/backups/shop-2026-08-23.dump
psql -U postgres -d shop_check -c "SELECT count(*) FROM users;"
dropdb -U postgres shop_check

对于 MySQL:gunzip -c dump.sql.gz | mysql -u root shop_check。

每月执行一次,并记录检查日期。

常见错误

现象原因
转储只有几 KB脚本在身份验证时失败,错误被输出到了 /dev/null
磁盘空间耗尽没有按 mtime 删除旧备份
恢复时因权限失败没有转储角色——您需要 --roles-only
Cron 静默运行,没有备份Cron 没有您的 PATH——请使用绝对路径
网站在夜间变慢转储争抢磁盘——请调整时间,添加 --quick

请在不同位置保留至少两份副本,并监控转储大小:大小骤降意味着并非整个数据库都被转储了。