适用:所有 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 把备份里的也删了 → 备份跟着一起错。 所以镜像只适合"防硬件故障",不适合"防误操作"。

这是 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

要检查的三件事:

  1. 文件能打开吗(不是 0 字节)
  2. 内容对吗(随机抽查几个)
  3. 文件数量对得上吗(差太多说明漏了)

一个自动校验脚本

#!/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
# 建测试数据
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 天完整计划