增量备份核心是比对文件修改时间戳,用os.stat获取fileinfo后调用modtime().after(lasttime)判断;需注意fat32/nfs等文件系统时间精度不足时漏更风险,应结合sha256校验和兜底,并妥善处理权限、时间戳、路径安全及打包校验。

增量备份的核心判断:用 os.Stat 和 FileInfo.ModTime() 比对时间戳
增量备份不是靠文件名或大小决定是否备份,而是依赖修改时间。Golang 中最可靠的方式是用 os.Stat 获取目标文件的 FileInfo,再比对 ModTime() 是否晚于上次备份记录的时间点。
注意:ModTime() 在某些文件系统(如 FAT32、NFS 挂载)上精度可能只有 1 秒或 2 秒,若源文件在同秒内被多次修改,会漏掉中间变更。生产环境建议搭配校验和(如 sha256.Sum256)做兜底。
- 备份前先读取一个本地时间戳文件(例如
last_backup_time),解析为time.Time - 遍历待备份目录时,对每个
os.FileInfo调用ModTime().After(lastTime) - 跳过符号链接(
fi.Mode()&os.ModeSymlink != 0),除非你明确需要备份链接本身 - 注意
os.ReadDir(Go 1.16+)比filepath.Walk更快且不自动跟随 symlink,适合大量小文件场景
备份文件写入:用 io.Copy 配合 os.Create + os.Chmod
直接 os.Copy 不可用 —— Go 标准库没有这个函数。实际做法是打开源文件(os.Open),创建目标文件(os.Create),再用 io.Copy 流式传输。
权限和时间戳不会自动继承。若需还原原始属性,必须显式调用 os.Chmod 和 os.Chtimes:
-
dstFile, _ := os.Create(dstPath)后立即dstFile.Chmod(srcFi.Mode()) -
os.Chtimes(dstPath, srcFi.ModTime(), srcFi.ModTime())设置访问/修改时间(第二个参数是 atime,第三个是 mtime) - 避免用
os.WriteFile,它会覆盖权限为 0644,且无法设置 mtime - 大文件建议加 buffer:
io.CopyBuffer(dstFile, srcFile, make([]byte, 32*1024))
跳过已备份文件:用 sha256 校验和做内容去重
仅靠时间戳不够鲁棒。比如文件被恢复成旧版本但时间戳未变,或编辑器保存时覆写但 ModTime 碰巧相同。这时需要内容级判断。
对每个待备份文件计算 sha256.Sum256,并把哈希值存进本地索引(如 SQLite 或简单 JSON 文件)。每次备份前查哈希是否存在,存在则跳过。
- 小文件可一次性
io.ReadAll计算;大文件必须流式计算(hash.Hash.Write+os.Open) - SQLite 表结构建议至少含:
path TEXT UNIQUE, hash BLOB, mtime INTEGER,用mtime辅助快速过滤(避免全表扫描) - 不要用文件名作为 key —— 同名不同内容、硬链接等情况会导致误判
- 校验和计算本身有开销,建议只对
ModTime()判定为“可能更新”的文件执行
备份路径组织:按日期分层 + 增量包打包为 .tar.gz
纯文件拷贝难管理。推荐每天生成一个独立目录(如 backup/2024-06-15/),当天所有增量文件都放入其中;再用 archive/tar + compress/gzip 打包压缩,最后删除原始目录节省空间。
打包时注意路径安全:目标文件路径必须以 ./ 开头或经过 filepath.Clean 处理,防止 ../../../etc/passwd 类路径穿越。
- 打包前用
filepath.Rel(baseDir, absPath)得到相对路径,确保写入 tar 的是干净路径 - gzip 压缩级别选
gzip.BestSpeed(默认)或gzip.BestCompression,别用NoCompression—— 增量备份本意就是省空间 - 打包后校验 tar.gz 是否可解压(
exec.Command("tar", "-tzf", archivePath)),失败则保留原始目录并报错 - 备份完成后更新
last_backup_time文件为当前时间(time.Now().UTC()),而非备份开始时间
增量逻辑本身不复杂,但时间精度、路径安全、权限还原、哈希一致性这四点,任何一个没处理好,都会让“备份可用”变成“备份存在但不能用”。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











