You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

IPFS共享更新文件的效率优化:仅传输变更块的方案问询

IPFS共享更新文件时如何仅传输变更块?

更新于2019年10月30日:

  • 关于IPFS中提升更新文件共享效率、减少块重复的类git-diff功能,社区已有相关需求讨论
  • 关于IPFS块级文件复制功能的细节,也可在社区讨论中找到更多信息

先给你还原一下核心场景:

  • 用户A用ipfs add file.txt上传了一个1GB的文件
  • 用户B把文件拉到本地后,用户A只修改了文件里一个字符,重新上传后,用户B却要重新下载整个1GB文件,而不是仅更新那一小部分

你肯定会问:IPFS能不能像git pull那样只拉取变更部分?毕竟Git用了增量压缩,IPFS有没有类似的机制?


验证实验

为了确认这个问题,我做了个小测试:

步骤1:初始上传与下载

用户A操作:

$ fallocate -l 1G gentoo_root.img
$ ipfs add gentoo_root.img
920.75 MB / 1024.00 MB [========================================>----] 89.92%
added QmdiETTY5fiwTkJeERbWAbPKtzcyjzMEJTJJosrqo2qKNm gentoo_root.img

用户B操作:

$ ipfs get QmdiETTY5fiwTkJeERbWAbPKtzcyjzMEJTJJosrqo2qKNm
Saving file(s) to QmdiETTY5fiwTkJeERbWAbPKtzcyjzMEJTJJosrqo2qKNm
1.00 GB / 1.00 GB [==================================] 100.00% 49s

步骤2:修改后重新上传与下载

用户A修改并重新上传:

$ echo 'hello' >> gentoo_root.img
$ ipfs add gentoo_root.img
# 这里节点显示推送完整1GB文件,耗时1小时,而非仅更新变更块
32.75 MB / 1.00 GB [=>---------------------------------------] 3.20% 1h3m34s
added Qmew8yVjNzs2r54Ti6R64W9psxYFd16X3yNY28gZS4YeM3 gentoo_root.img

用户B获取新文件:

# 还是要下载完整1GB文件
ipfs get Qmew8yVjNzs2r54Ti6R64W9psxYFd16X3yNY28gZS4YeM3
[sudo] password for alper:
Saving file(s) to Qmew8yVjNzs2r54Ti6R64W9psxYFd16X3yNY28gZS4YeM3
1.00 GB / 1.00 GB [=========================] 100.00% 45s

核心问题

这种场景下,怎么避免重新共享完整文件,只传输变更的数据块?最优方案是什么?

另外还有个小问题:同一节点执行ipfs cat <hash>时,每次都会重新下载相同的哈希:

$ ipfs cat Qmew8yVjNzs2r54Ti6R64W9psxYFd16X3yNY28gZS4YeM3
212.46 MB / 1.00 GB [===========>---------------------------------------------] 20.75% 1m48s
$ ipfs cat Qmew8yVjNzs2r54Ti6R64W9psxYFd16X3yNY28gZS4YeM3
212.46 MB / 1.00 GB [===========>---------------------------------------------] 20.75% 1m48s

仓库增量分析

我还对比了原文件和更新后文件对仓库大小的影响:

  1. 先创建100MB的file.txt,初始仓库状态:
NumObjects: 5303
RepoSize: 181351841
StorageMax: 10000000000
RepoPath: /home/alper/.ipfs
Version: fs-repo@6
  1. 添加并固定文件后:
$ ipfs add file.txt
added QmZ33LSByGsKQS8YRW4yKjXLUam2cPP2V2g4PVPVwymY16 file.txt
$ ipfs pin add QmZ33LSByGsKQS8YRW4yKjXLUam2cPP2V2g4PVPVwymY16
  1. 仓库状态变为:
$ ipfs repo stat
NumObjects: 5307
RepoSize: 181389824
StorageMax: 10000000000
RepoPath: /home/alper/.ipfs
Version: fs-repo@6

对象数增加4,仓库大小增加了37983字节。

  1. 执行echo 'a' >> file.txt后再次添加并固定,发现仓库大小的增量和首次添加时完全一致。

解决方案与最优实践

1. 理解IPFS的块级复用机制

首先要明确:IPFS默认把文件分割成固定大小的块(默认256KB),每个块用内容哈希标识。只要块内容不变,哈希就不变——也就是说,重新上传修改后的文件时,IPFS会自动复用本地或网络中已存在的块,只上传变化的块。

你实验中看到的进度条显示整个文件大小,其实是个“视觉错觉”:进度条统计的是文件总大小,但实际传输的只有变化的块。如果你查看ipfs repo stat的前后变化,会发现仓库大小只增加了几KB(新增内容的块+新的根节点块),而不是1GB。如果你的节点真的上传了整个1GB,可能是缓存或配置问题,可以检查IPFS的日志确认实际传输量。

2. 使用ipfs patch实现增量更新

IPFS提供了patch子命令,可以直接修改已有的IPFS文件对象,不需要重新上传整个文件:

  • 在文件末尾添加内容:ipfs patch append <原文件哈希> <新增内容文件>
  • 替换文件中的某段内容:ipfs patch replace <原文件哈希> <起始偏移> <长度> <替换内容文件>

这样生成的新文件对象会引用原文件的大部分块,只包含变化的部分。用户B获取新文件时,只会下载新增或修改的块,不需要重新下载整个文件。

3. 结合Git与IPFS

既然你熟悉Git的增量更新,可以把Git仓库托管到IPFS中:

  • 每次提交后,用ipfs add上传Git仓库的最新版本
  • 用户B可以先拉取IPFS中的仓库,再用git pull获取增量更新,结合两者的优势
  • 也可以用专门的工具让Git直接把IPFS作为远程仓库,实现无缝的增量传输

4. 调整块大小适配修改场景

默认块大小是256KB,如果你的修改通常很小(比如只改几个字符),可以尝试减小块大小(比如64KB),这样修改只会影响更少的块。但要注意:块太小会导致块数量激增,增加节点的存储和索引负担,需要根据实际场景权衡。

5. 解决ipfs cat重复下载的问题

ipfs cat默认不会自动固定文件到本地仓库,所以每次运行都会从网络重新获取。解决方法很简单:先执行ipfs pin add <文件哈希>,把文件固定到本地,后续再用ipfs cat就会直接读取本地存储,不会重新下载了。


内容的提问来源于stack exchange,提问作者alper

火山引擎 最新活动