适用:所有 Linux 耗时:约 30 分钟 前置:T4(会用 rsync)、E5(懂磁盘)
备份这件事,只有两种状态:做了,和后悔。
这一篇讲三件事:备份什么、备份到哪、怎么自动做。
一、先破除三个幻觉
幻觉 1:「我的文件很重要,所以我不会丢」
重要性和安全性无关。 硬盘不会因为你重视它就少坏一点。
统计数据(各厂商综合):机械硬盘年故障率约 1-2%,但这意味着一块用了 5 年的盘,累积故障概率到 5-10%。加上误删、误格式化、勒索软件、系统重装——“不会丢"是错觉。
幻觉 2:「我把文件拷到移动硬盘了,安全了」
拷过去那天安全,之后就没人管了。
真实情况:大部分人的"备份"是两年前拷过一次的移动硬盘,躺在抽屉里。 里面的数据早就过期,而且那块硬盘本身也可能坏了。
备份不是一次动作,是一个持续的过程。
幻觉 3:「云盘同步就是备份」
同步 ≠ 备份。
| 同步 | 备份 | |
|---|---|---|
| 你删了文件 | 云端也删 | 历史版本还在 |
| 你改错了 | 云端也变错 | 旧版本可恢复 |
| 勒索软件加密 | 云端也被加密 | 离线副本安全 |
| 误操作 | 立刻传播 | 有多个时间点 |
同步是"多设备一致”,备份是"能回到过去"。 两者不能互相替代。
二、3-2-1 原则:一句话记住
3 份副本,2 种介质,1 份异地。
| 数字 | 含义 | 举例 |
|---|---|---|
| 3 | 数据要有 3 份(1 份原始 + 2 份备份) | 电脑 + 移动硬盘 + 云 |
| 2 | 至少 2 种不同介质 | 硬盘 + 云(不要两块硬盘放一起) |
| 1 | 至少 1 份在异地 | 家里着火/被盗,异地那份还在 |
为什么强调"异地":火灾、水淹、盗窃、雷击——这四种情况下,同屋的所有备份同时消失。
小规模也能做到:电脑 + 一块移动硬盘(家里)+ 一份加密后放云上(异地)。这就满足 3-2-1 了。
三、第一步:想清楚备份什么
别一上来就备份整个 /。 系统文件可以重装,你真正丢不起的是"自己产生的数据"。
值得备份的(优先级从高到低)
| 优先级 | 内容 | 位置 |
|---|---|---|
| 1 | 个人文档 | ~/文档 ~/桌面 ~/下载 |
| 2 | 照片和视频 | ~/图片 |
| 3 | 代码和项目 | ~/项目 ~/code |
| 4 | 配置文件 | ~/.bashrc ~/.ssh ~/.config |
| 5 | 应用数据 | ~/.local/share(浏览器书签等) |
| 6 | 数据库(如果有) | /var/lib/mysql 等 |
不需要备份的
- ❌ 系统本身(重装就行,半小时)
- ❌
/usr/lib/bin(软件包能重下) - ❌
/tmp(临时文件) - ❌
~/.cache(缓存,重建即可) - ❌
/var/log(日志,除非排查问题需要)
知道"不用备份什么",比知道"备份什么"更重要——不然备份会变得又大又慢,你就坚持不下去。
一条命令看清家目录谁最占地方
du -sh ~/* 2>/dev/null | sort -h | tail -15
四、方法 1:rsync(本地/远程,最灵活)
T4 讲过 rsync,这里讲怎么用于备份。
基础:镜像备份
rsync -av --delete ~/文档/ /mnt/backup/文档/
--delete 让备份和目标完全一致。
⚠️ 但这是"镜像",不是"历史"。 你误删了文件 →
--delete把备份里的也删了 → 备份跟着一起错。 所以镜像只适合"防硬件故障",不适合"防误操作"。
带历史:用 --link-dest 做增量快照
这是 rsync 最有价值的一个技巧(很多人不知道):
#!/bin/bash
set -euo pipefail
SRC="$HOME/文档/"
DEST="/mnt/backup/文档"
TODAY=$(date +%Y-%m-%d)
TARGET="$DEST/$TODAY"
# 找到最近一次备份(用于硬链接)
LATEST=$(ls -1d "$DEST"/20??-??-?? 2>/dev/null | sort | tail -1 || true)
mkdir -p "$TARGET"
if [ -n "$LATEST" ] && [ -d "$LATEST" ]; then
# 有历史:和上次比较,没变的文件用硬链接(不占额外空间)
rsync -av --delete \
--link-dest="$LATEST" \
"$SRC" "$TARGET/"
else
# 第一次:全量
rsync -av "$SRC" "$TARGET/"
fi
echo "✅ 完成: $TARGET"
du -sh "$TARGET"
效果:
/mnt/backup/文档/
├── 2026-10-18/ ← 完整快照(实际占全部空间)
├── 2026-10-19/ ← 只有变化的文件占空间,其余是硬链接
└── 2026-10-20/ ← 同上
每次都是一份"完整的快照",但新增的磁盘占用只有"变化的那些文件"。
这就是 Time Machine 的原理。 用 rsync 一个参数就实现了。
硬链接的妙处:多个快照里的同一个文件,指向同一份数据。删掉某个快照不影响其他快照。
保留策略:别让备份无限增长
# 只保留最近 30 份
ls -1d "$DEST"/20??-??-?? | sort | head -n -30 | xargs -r rm -rf
放在脚本末尾。 30 天滚动,磁盘占用可控。
五、方法 2:tar 打包(适合云存储)
云存储一般按文件存不方便,打包成一个归档更好。
#!/bin/bash
set -euo pipefail
DEST="$HOME/backup"
mkdir -p "$DEST"
NAME="backup-$(date +%Y%m%d).tar.gz"
tar -czf "$DEST/$NAME" \
--exclude='*.log' \
--exclude='.cache' \
--exclude='node_modules' \
-C "$HOME" \
文档 桌面 图片 项目 .bashrc .ssh .config
echo "✅ $DEST/$NAME ($(du -h "$DEST/$NAME" | cut -f1))"
# 只留最近 7 份
ls -1t "$DEST"/backup-*.tar.gz | tail -n +8 | xargs -r rm -f
优点:单个文件,好传好存。 缺点:每次都全量(除非增量 tar,那很复杂),大目录会很慢。
适合:中小数据量(几 G 以内)、需要上传云端的场景。
⚠️ 打包 .ssh 要加密
私钥打包进 tar.gz 是不安全的(尤其上传云盘):
# 用 GPG 加密
tar -czf - 文档 .ssh | gpg -c --cipher-algo AES256 > backup-$(date +%Y%m%d).tar.gz.gpg
# 解密
gpg -d backup-20261020.tar.gz.gpg | tar -xzf -
gpg -c 会问你一个密码。这个密码要单独记住/记在别处,不能和备份放一起。
六、方法 3:快照(如果文件系统支持)
Btrfs / ZFS 支持"瞬时快照" —— 几乎不占空间,删了也不影响其他快照。
# Btrfs
sudo btrfs subvolume snapshot /home /backup/home-2026-10-20
# 查看
sudo btrfs subvolume list /
优点:瞬时(毫秒级)、几乎零开销、能回到任意时间点。 缺点:快照在同一块盘上——盘坏了快照一起没。 所以不能替代异地备份,只能作为"防误操作"的第一层。
七、自动化:cron 定时跑
手动备份 = 不会备份。 一定要自动化。
完整脚本
mkdir -p ~/bin
nano ~/bin/backup.sh
#!/bin/bash
# 备份家目录重要文件,保留 30 天历史
set -euo pipefail
SRC="$HOME/文档/"
DEST="/mnt/backup/文档" # ← 改成你的备份位置
KEEP=30
LOG="$HOME/.backup.log"
exec >> "$LOG" 2>&1
echo "=== $(date '+%Y-%m-%d %H:%M:%S') 开始备份 ==="
# 检查目标是否挂载(重要!不然会往根分区写,把盘写满)
if [ ! -d "$DEST" ]; then
echo "❌ 备份目录不存在: $DEST"
exit 1
fi
if ! mountpoint -q "$(dirname "$DEST")" 2>/dev/null && [ "$(df --output=target "$DEST" | tail -1)" = "/" ]; then
echo "⚠️ 警告: $DEST 可能在根分区上,不是独立备份盘"
fi
TODAY=$(date +%Y-%m-%d)
TARGET="$DEST/$TODAY"
LATEST=$(ls -1d "$DEST"/20??-??-?? 2>/dev/null | sort | tail -1 || true)
mkdir -p "$TARGET"
if [ -n "$LATEST" ] && [ -d "$LATEST" ]; then
rsync -a --delete --link-dest="$LATEST" "$SRC" "$TARGET/"
else
rsync -a "$SRC" "$TARGET/"
fi
# 清理旧备份
ls -1d "$DEST"/20??-??-?? 2>/dev/null | sort | head -n -"$KEEP" | xargs -r rm -rf
SIZE=$(du -sh "$TARGET" | cut -f1)
TOTAL=$(du -sh "$DEST" | cut -f1)
echo "✅ 完成: $TARGET ($SIZE) | 备份总占用: $TOTAL"
chmod +x ~/bin/backup.sh
~/bin/backup.sh # 先手动跑一次
tail -20 ~/.backup.log # 看日志
加到 cron
crontab -e
# 每天凌晨 2 点备份
0 2 * * * /home/你的用户名/bin/backup.sh
验证 cron 跑了:
tail -30 ~/.backup.log
grep CRON /var/log/syslog | tail -5
八、一定要测试恢复
没恢复过的备份,不能算备份。
这是备份领域最硬的规矩。
定期演练(每季度一次)
# 1. 找一个测试目录
mkdir -p /tmp/restore-test
# 2. 从备份里恢复一份
rsync -av /mnt/backup/文档/2026-10-20/ /tmp/restore-test/
# 3. 随机打开几个文件,确认能读、内容对
cat /tmp/restore-test/某个重要文件.txt
# 4. 对比文件数量
find /mnt/backup/文档/2026-10-20 -type f | wc -l
find ~/文档 -type f | wc -l
要检查的三件事:
- 文件能打开吗(不是 0 字节)
- 内容对吗(随机抽查几个)
- 文件数量对得上吗(差太多说明漏了)
一个自动校验脚本
#!/bin/bash
# 验证最新备份的完整性
set -euo pipefail
DEST="${1:-/mnt/backup/文档}"
LATEST=$(ls -1d "$DEST"/20??-??-?? | sort | tail -1)
echo "检查: $LATEST"
echo "文件数: $(find "$LATEST" -type f | wc -l)"
echo "总大小: $(du -sh "$LATEST" | cut -f1)"
echo "空文件数(可能是损坏的): $(find "$LATEST" -type f -empty | wc -l)"
echo "--- 空文件列表(如果有)---"
find "$LATEST" -type f -empty | head -20
九、一个真实的备份方案(照抄)
给普通办公用户的最小可行方案:
第一层:每天自动 rsync 到家中的移动硬盘(--link-dest 保留 30 天)
第二层:每周打包加密,上传到云盘(坚果云/OneDrive/阿里云盘)
第三层:重要文档额外放一份在 U 盘(每季度更新)
成本:一块 1T 移动硬盘(约 400 元)+ 免费云盘空间。 时间:配置 30 分钟,之后全自动。
这已经满足 3-2-1 了。
十、动手:五个练习
练习 1:盘点你要备份什么
du -sh ~/* 2>/dev/null | sort -h | tail -15
写下哪些是"丢了会心疼"的。
练习 2:手动做一次镜像备份
mkdir -p /tmp/backup-test
rsync -av --delete ~/文档/ /tmp/backup-test/文档/ 2>/dev/null || echo "没有文档目录,换个目录试"
ls /tmp/backup-test/文档/ | head
练习 3:体验 --link-dest 的空间节省
# 建测试数据
mkdir -p /tmp/bk/src && echo "a" > /tmp/bk/src/a.txt && echo "b" > /tmp/bk/src/b.txt
# 第一次全量
rsync -av /tmp/bk/src/ /tmp/bk/snap1/
du -sh /tmp/bk/snap1
# 改一个文件,第二次带硬链接
echo "changed" > /tmp/bk/src/a.txt
rsync -av --link-dest=/tmp/bk/snap1/ /tmp/bk/src/ /tmp/bk/snap2/
# 看两个快照的占用
du -sh /tmp/bk/snap1 /tmp/bk/snap2
du -sh /tmp/bk/ # 总量应该明显小于两者相加
du -sh 会显示两个快照都"占满",但总目录小得多——这就是硬链接的效果。
验证硬链接:
ls -li /tmp/bk/snap1/b.txt /tmp/bk/snap2/b.txt
# 第一列 inode 相同 → 是同一个文件的两个名字
练习 4:写并跑备份脚本
把第七节的脚本保存成 ~/bin/backup.sh,改成你的路径,跑一次,看日志。
练习 5:恢复演练
mkdir -p /tmp/restore
rsync -av /tmp/bk/snap2/ /tmp/restore/
cat /tmp/restore/a.txt # 应该是 changed
find /tmp/restore -type f | wc -l
十一、验证:你过关了吗
- 知道 3-2-1 原则
- 能分清"同步"和"备份"
- 知道自己该备份哪些目录(不是整个系统)
- 会用
rsync --link-dest做增量快照 - 会把备份脚本加到 cron
- 做过一次恢复演练
- 知道
--delete的镜像备份不能防误删
十二、不行怎么办
Q1:备份脚本跑完,备份目录是空的 最常见原因:备份盘没挂载。检查:
df -h /mnt/backup
mount | grep backup
没挂载的话,rsync 会往根分区的空目录写,把系统盘写满。
Q2:rsync: No space left on device
备份盘满了。加保留策略(第七节的 KEEP=30),或者清理旧备份:
ls -1d /mnt/backup/文档/20??-??-?? | sort | head -n -10 | xargs rm -rf
Q3:备份跑得很慢
- 加
--exclude排除大而无用的目录(node_modules、.cache) - 首次全量慢是正常的,之后增量会快很多
- 网络备份(rsync over ssh)受带宽限制,可以
--bwlimit
Q4:cron 没执行 三个原因:
# ① 脚本没执行权限
ls -l ~/bin/backup.sh
# ② 路径不对(cron 的 PATH 很短,脚本里要用绝对路径)
# ③ cron 服务没跑
systemctl status cron
查 cron 日志:
grep CRON /var/log/syslog | tail -10
Q5:备份文件比原文件小很多,是不是漏了 先确认是不是用了压缩。如果没压缩,检查:
# 对比文件数量
find 原目录 -type f | wc -l
find 备份目录 -type f | wc -l
差太多就说明 --exclude 排多了,或者有权限问题读不到某些文件。
Q6:恢复后发现文件权限不对
rsync 的 -a 包含 -p(保权限),但如果跨用户恢复:
sudo rsync -av --no-perms --chown=你的用户:你的组 备份/ 目标/
Q7:怎么备份正在运行的程序数据(比如数据库) 不能直接拷文件(可能拷到不一致的状态)。用程序自己的导出工具:
# MySQL
mysqldump -u root -p 数据库名 > backup.sql
# SQLite(先确保没有进程在写)
sqlite3 数据库.db ".backup backup.db"
十三、一句话总结
3 份副本、2 种介质、1 份异地。
备份"自己产生的数据",不备份系统。
rsync --link-dest做增量快照——每次完整,只占变化的空间。没恢复过的备份,不能算备份。
← 上一篇 T6|软件源 → 第四周 架起来:服务与自动化 总课表 28 天完整计划