IPFS在Merkle DAG上对版本化的文件系统进行了建模。这个模型与Git类似:
文件对象被存储时一般都要进行数据分割,每块大小为256kb,但如果是小文件(< 256kb)就不需要做分割处理,直接将其作为一个数据单元进行存储,存储的类型就是blob对象。大文件切割后的数据单元也是blob对象。
每个blob对象包含的是一个可寻址的数据单元,如下所示::
{
"data":"some data here",
//blobs 无links
}
文件大于256kb的大文件在ipfs存储时,需要进行数据分割。list对象由几个blob对象组成,其中里面的blob可能有重复。lists对象包含有序的blob和list对象。这些有序的blob和list会有自己的link,link对应了他们的数据信息。
如下结构标示了一个lists结构:
{
"data":["blob","list","blob"], //lists有一个对象类型的数组作为数据
"links":[
{
"hash":"XLYkgq61DYaq8Nhkcqy7LcnSA7dSHQ78x",
"size":189458
},
{
"hash":"XLHBNsgoepUDKL8dkd9Hesa5io9sdxi7n",
"size":19442
},
{
"hash":"XLWVQKJII8v7dggkfdhHSFlkaw9yjs7dj",
"size":5286
} // 在links中lists是没有名字的
]
}
在IPFS中,Tree对象与Git的tree类似,代表一个目录,或者一个名字到哈希值的映射表,哈希值表示blob,list,其他的tree,或commit,结构如下:
{
"data":["blob","list","blob"], // trees有一个对象类型的数组作为数据
"links":[
{
"hash":"XLYkgq61DYaq8Nhkcqy7LcnSA7dSHQ78x",
"name":"less",
"size":189458
},
{
"hash":"XLHBNsgoepUDKL8dkd9Hesa5io9sdxi7n",
"name":"script",
"size":19442
},
{
"hash":"XLWVQKJII8v7dggkfdhHSFlkaw9yjs7dj",
"name":"template",
"size":5286
} // trees是没有名字的
]
}
在IPFS中,commit对象代表任何对象在版本历史记录中的一个快照,它与Git的commit也非常类似,但它可以指向任何类型的对象。
{
"data": {
"type": "tree",
"date": "2014-09-20 12:44:06Z",
"message": "This is a commit message."
}, "links": [
{ "hash": "XLa1qMBKiSEEDhojb9FFZ4tEvLf7FEQdhdU",
"name": "parent", "size": 25309 },
{ "hash": "XLGw74KAy9junbh28x7ccWov9inu1Vo7pnX",
"name": "object", "size": 5198 },
{ "hash": "XLF2ipQ4jD3UdeX5xp1KBgeHRhemUtaA8Vm",
"name": "author", "size": 109 }
]
}
> ipfs file-cat <ccc111-hash> --json
{
"data": {
"type": "tree",
"date": "2014-09-20 12:44:06Z",
"message": "This is a commit message."
}, "links": [
{ "hash": "<ccc000-hash>",
"name": "parent", "size": 25309 },
{ "hash": "<ttt111-hash>",
"name": "object", "size": 5198 },
{ "hash": "<aaa111-hash>",
"name": "author", "size": 109 }
]
}
> ipfs file-cat <ttt111-hash> --json
{
"data": ["tree", "tree", "blob"],
"links": [
{ "hash": "<ttt222-hash>",
"name": "ttt222-name", "size": 1234 },
{ "hash": "<ttt333-hash>",
"name": "ttt333-name", "size": 3456 },
{ "hash": "<bbb222-hash>",
"name": "bbb222-name", "size": 22 }
]
}
> ipfs file-cat <bbb222-hash> --json
{
"data": "blob222 data",
"links": [] }
IPFS的版本控制是在Git的基础上实现的,Git版本控制在之前的章节有讲。commit对象代表着一个对象在历史版本中的一个特定快照。两个不同的commit之间相互比较对象数据,可以揭露出两个不同版本文件系统的区别。IPFS可以实现Git版本控制工具的所有功能,同时也可以兼容Git。
兼容的原因可能是:
在Merkle DAG中,我们可以看到,IPFS对象可以使用字符串路径API来遍历。IPFS文件对象是特意设计的,为的是让挂载IPFS到UNIX文件系统更加简单。
版本控制和分发大文件最主要的挑战:找到一个正确的方法来将它们分隔成独立的块。与其认为IPFS可以为每个不同类型的文件提供正确的分隔方法,不如说IPFS提供了以下的几个可选选择:
使用Rabin Fingerprints指纹算法来定义比较合适的块边界。 使用rsync和rolling-checksum算法,来检测块在版本之间的改变。 允许用户设定文件大小而调整数据块的分割策略。
基于路径的访问需要遍历整个对象图,检索每个对象需要在DHT中查找它的Key值,连接到对等点并检索对应的数据块。这是一笔相当大的性能开销,特别是在查找的路径具有多个路径时。IPFS充分考虑了这一点,并设计了如下的方式来缓解:
例如,对于上面的ttt111的扁平树如下:
{
"data":["tree","blob","tree","list","blob","blob"],
"links":[
{"hash":"<ttt222-hash>","size":1234,"name":"ttt222-name"},
{"hash":"<bbb111-hash>","size":123,"name":"ttt222-name/bbb111-name"},
{"hash":"<ttt333-hash>","size":3456,"name":"ttt333-name"},
{"hash":"<lll111-hash>","size":578,"name":"ttt333-name/lll111-name"},
{"hash":"<bbb222-hash>","size":22,"name":"ttt333-name/lll111-name/bbb222-name"},
{"hash":"<bbb222-hash>","size":22,"name":"bbb222-name"},
]
}