restic 是一个单文件的备份工具,11237567 字节的 exe,支持本地目录、SFTP、S3、B2 等几十种后端。它的核心是内容寻址的去重:同样的数据块只存一次。

拿 500 个文件、142169088 字节的数据做完整实测:初始化仓库 3.57 秒,首次备份 3.09 秒,改一个文件再备份 1.29 秒且只新增 82.662 KiB,恢复全部数据 4.84 秒。

一个 exe,135.6 MB 数据

restic --version
restic 0.19.1

restic init --repo D:\rk_repo
created restic repository 9c1f... at D:\rk_repo
项目数值
exe 体积11237567 字节
源数据142169088 字节(500 个文件)
文件分布20 个目录,单个 1 KB 到 1 MB 混合
初始化仓库3.57 秒
仓库体积(空)约 1.1 MB

初始化只花 3.57 秒,做的事就是创建一组目录结构加几个配置文件。仓库本身占 1 MB 左右,全部是元数据。

它没有守护进程,也没有数据库服务,所有状态都在那个仓库目录里。备份完把目录拷走,换台机器装上同版本 exe 就能读。

首次备份 3.09 秒,仓库几乎 1:1

restic backup D:\rk_src --repo D:\rk_repo
processed 500 files, 135.583 MiB in 0:02
snapshot 441dc709 saved
指标数值换算
备份耗时3.09 秒约 44 MB/s
处理文件数500 个—
源数据142169088 字节135.6 MiB
写入仓库135.929 MiB(实存 135.672 MiB)备份报告值
新文件 / 新目录500 个 / 21 个全部首次入库

1.001 倍这个数字说明压缩几乎没起作用。原因是测试数据用 os.urandom 生成,本身已经是随机字节,没有任何可压缩的冗余。换成文档、日志、代码这类真实数据,压缩比通常在 2 到 3 倍。

44 MB/s 这个速度包含了读取 500 个文件、切块、算哈希、写入仓库全过程。单个文件越小,文件系统的元数据开销占比越高,所以小文件多的时候速度会掉下来。

改一个文件,第二次只加 0.4 MB

在第一次备份之后,往其中一个 4 KB 的文件末尾追加 4096 字节,再备份一次:

指标第一次第二次(追加 4096 字节)
耗时3.09 秒1.29 秒
文件变化500 new / 0 changed0 new / 1 changed / 499 unmodified
本次写入135.929 MiB82.662 KiB(实存 46.267 KiB)
增量占总量—0.06%

只在一个文件末尾追加了 4096 字节,实际写进仓库的是 82.662 KiB,比 4 KB 多了 20 倍。原因是 restic 按内容切块,改动会让后续块的边界偏移,连带重新存储若干块。这是内容寻址备份的固有代价,改文件头比改文件尾影响更大。

即便如此,0.06% 的增量也很划算:135.6 MiB 的数据,日常改一次只多存几十 KB。耗时从 3.09 秒降到 1.29 秒,因为 499 个文件的块算完哈希发现已存在,直接跳过写入。

恢复 4.84 秒,142173184 字节

restic restore latest --target D:\rk_restore --repo D:\rk_repo
restoring snapshot 441dc709 of [...]
Summary: Restored 502 files/dirs (135.6 MiB) in 0:04
指标数值
恢复耗时(首次)4.84 秒
恢复耗时(热缓存)1 秒内
还原字节数142173184 字节
还原文件数521 个(含目录)
与源数据差异4160 字节(就是后来追加的那部分)

首次恢复 4.84 秒,接着再恢复一次只要 1 秒内,因为文件还在系统缓存里。restic 自己的输出会把秒数取整,命令里看到的是 Restored 521 files/dirs (135.591 MiB) in 0:01。走网络后端时会受带宽限制,这个数字会明显变大。

还原后的字节数比源数据多 4160 字节,正好是第二次备份前追加的 4096 字节加上一点对齐开销。说明恢复的是最新快照,数据对得上。

压缩比 1.00x 是怎么来的

restic stats --repo D:\rk_repo --mode raw-data
Snapshots processed:  2
   Total Blob Count:  577
Total Uncompressed Size:  136.374 MiB
              Total Size:  136.046 MiB
    Compression Progress:  100.00%
       Compression Ratio:  1.00x

restic 0.14 之后默认开启压缩(zstd),但随机数据压不动,所以比值停在 1.00x。577 个块对应 500 个文件,说明平均每个文件切成 1 块多一点,块大小在 1 MB 上下。

换成可压缩的数据,这个比值会明显变化。135.6 MiB 的纯文本大概能压到 40 到 60 MiB,压缩比 2 到 3 倍。这也意味着用随机数据测出来的速度会偏保守,真实场景通常更快。

它和 kopia、duplicati 的分工

站内另外两篇分别是 kopia 和 duplicati。三者的共同点是都做增量、都支持加密、都能对接多种存储。区别在交互模型:restic 是纯命令行加脚本,没有常驻服务;duplicati 带 Web 界面,适合不想写脚本的人;kopia 两者都有。

restic 的优势是可脚本化:一行命令备份,一行命令恢复,退出码明确,适合放进定时任务或者 CI。代价是所有参数都要记,第一次用得翻文档。

它不含调度器,定时备份要靠系统计划任务或者 cron。这一点和 duplicati 相反,后者自带调度面板。

三条使用前必须知道的事

第一,仓库密码丢了数据就打不开,没有找回机制。密码存在环境变量 RESTIC_PASSWORD 里最省事,但意味着任何能读到这个变量的人都能解密备份。

第二,forget 和 prune 是两个动作。删快照只是标记,真正回收空间要跑 prune,而 prune 会重写仓库、耗时明显长于备份。135.6 MiB 的仓库 prune 一次通常要十几秒。

第三,备份前想清楚要排除什么。默认会把目录里所有东西都收进去,包括缓存和临时文件,那些东西天天变,会让每次备份都产生大量新块,去重效果大打折扣。