核心是用元数据指纹+状态快照实现轻量差异识别:以inode/mtime/size三元组初筛,跨文件系统时降级为path+mtime+size组合判断;结合rsync批处理原子推送与SQLite管理去重状态,支持千万级毫秒查询,并自动清理陈旧快照。

针对千万级小文件的增量同步与排重,核心不是“每次全量比对”,而是用元数据指纹+状态快照构建轻量级差异识别机制。重点在于避免遍历和哈希全量内容,同时保证一致性。
用增量文件列表 + inode/mtime/size 三元组做快速初筛
小文件场景下,不建议直接对每个文件计算 md5/sha256(I/O 和 CPU 开销过大)。可先利用文件系统原生属性做低成本过滤:
- 用
find /src -maxdepth 1 -type f -printf '%i %T@ %s %p\n'生成源端快照:记录 inode、纳秒级修改时间、字节大小、路径 - 目标端用同样命令生成当前快照,保存为
dst.snap - 用
awk对比两快照:仅当 inode 相同 且 mtime 和 size 完全一致,才视为已存在(适用于同一文件系统或硬链接复用场景) - 若跨文件系统(inode 无效),则降级为 “path + mtime + size” 组合判断,误判率可控(小文件同名同大小同修改时间概率极低)
用 rsync --write-batch + 状态标记实现原子化增量推送
避免并发写入导致中间态不一致,也不依赖锁文件:
- 先执行
rsync -av --dry-run --out-format='%n' /src/ user@dst:/dst/ | sort > diff.list获取待同步路径列表 - 用
rsync -av --write-batch=sync.batch /src/ user@dst:/dst/生成批量指令包(含校验和与元数据) - 在目标端执行
rsync --read-batch=sync.batch /dst/,该操作原子完成,失败时 batch 包可重放 - 同步成功后,将本次
diff.list的 SHA256 写入/dst/.last_sync_hash,作为下次初筛依据
用 SQLite 替代文本日志管理去重状态,支持千万级快速查询
不用 grep 扫描大日志,建轻量本地数据库存关键指纹:
- 初始化:
sqlite3 dedupe.db "CREATE TABLE IF NOT EXISTS files (path TEXT PRIMARY KEY, mtime REAL, size INTEGER, hash TEXT);" - 每次同步前,用
find输出的三元组批量插入或 REPLACE: find /src -type f -printf 'REPLACE INTO files VALUES ("%p", %T@, %s, "");\n' | sqlite3 dedupe.db- 同步后,对新增/变更文件抽样计算 hash(如每千个取1个,或只对 size > 0 且 mtime 变更的算)并更新到 DB
- 下轮初筛时,直接
SELECT path FROM files WHERE mtime = ? AND size = ?,毫秒级响应
自动清理陈旧快照与失效状态,防止磁盘膨胀
千万级文件的元数据本身也需治理:
- 快照文件按日期压缩归档,保留最近 3 天的明文快照,更早的仅留
.snap.gz - SQLite DB 每周 vacuum,并执行
DELETE FROM files WHERE mtime (若业务允许 7 天内删除的文件不参与排重) - 用
inotifywait监听源目录,实时捕获新增/修改/删除事件,异步更新 DB,减少全量扫描频次
不复杂但容易忽略:真正影响性能的往往不是同步本身,而是元数据采集方式和状态存储结构。用好 inode/mtime/size 三元组 + SQLite + rsync batch,可在不牺牲一致性的前提下,把千万小文件的日级同步从小时级压到十分钟内。











