我想开始讨论将存储库格式更改为版本 2。这是支持压缩所必需的(参见 #21)。
以下列表将在新提案出现时更新。
公认:
README
文件添加到描述此目录包含的内容的新存储库中。要讨论的:
推迟/拒绝:
还要别的吗?
我不确定将标题移到前面。 我知道这目前还没有实现,但是对于本地存储库,在末尾有标头意味着我们可以保存文件副本。
有趣的一点,谢谢。 我还不确定如何判断哪个更好。 对于远程后端,我们也可以(在对后端接口进行一些更改后)只传递一个io.Reader
,然后 stdlib 可以使用sendfile
直接从磁盘流式传输文件。 嗯。
仅供参考,我一直想知道您为什么不使用 GCM,所以我运行了基准测试。 如果 CPU 没有 AES-NI(比 Go 内置 GCM 快 50%),则 AES-CTR + Poly1305 相当快。 使用 AES-NI,Go 为 GCM 优化的汇编代码可能是无与伦比的。
英特尔至强 E312xx
restic:
BenchmarkEncrypt-4 50 32470322 ns/op 258.35 MB/s
stupidgcm:
Benchmark4kEncStupidGCM-4 200000 10620 ns/op 385.67 MB/s
Benchmark4kEncGoGCM-4 300000 5540 ns/op 739.22 MB/s
英特尔奔腾 G630(无 AES-NI)
restic:
BenchmarkEncrypt-2 10 108468078 ns/op 77.34 MB/s
stupidgcm:
Benchmark4kEncStupidGCM-2 50000 24182 ns/op 169.38 MB/s
Benchmark4kEncGoGCM-2 20000 96391 ns/op 42.49 MB/s
这不属于这个问题,但我还是会回答:
我想在我开始 restic 的时候,Go 并没有 GCM 的优化版本。 此外,我不习惯使用 GCM,因为我不理解它,而 Poly1305 论文更容易阅读和理解。
我认为您的基准测试处理的数据块要小得多,也许当块更大时它会更接近。
在索引文件或包页脚中具有未压缩的大小是否重要? 我认为只在 blob 中知道它是可以的,那么在 restic 中需要更少的更改。 它是否使任何新功能能够在这个额外的地方知道?
在我看来,存储库格式 2 == blob 数据的第一个字节表示压缩格式,这就是所需要的。 也许 255 种可能的格式之一可能是 {64-bits uncompressed length}{compressed data}。
我认为纠错是备份的好主意。 但我认为这是文件系统的责任。 你也想在restic里面实现RAID吗?
在索引文件或包页脚中具有未压缩的大小是否重要?
是:包标头描述了包中的内容,这告诉了提取过程预期的内容(根据压缩算法、未压缩大小以及后来的其他属性,例如用于加密的密钥)。 同样需要在索引中表示,已引入该索引以便 restic 不需要查找包标头中的每个 blob。 所以同样的信息需要在那里出现。
在我看来,存储库格式 2 == blob 数据的第一个字节表示压缩格式,这就是所需要的。 也许 255 种可能的格式之一可能是 {64-bits uncompressed length}{compressed data}。
我不喜欢这个想法,它使文件格式更加复杂:我们将在两个不同的地方拥有控制信息:在 blob 的开头和在标题中。 标头正是包含控制信息的位置。
我认为纠错是备份的好主意。 但我认为这是文件系统的责任。
原则上我同意,但文件系统是非常复杂的东西,并且错误传播(例如介质的读/写错误)通常不是最佳的。 对于高度减少(在冗余方面,例如去重)备份数据,我仍然认为添加(或提议添加)另一层纠错是个好主意。
对于 Reed-Solomon 代码,在https://github.com/klauspost/reedsolomon有一个纯 Go 实现以及一些性能数据。
根据https://www.usenix.org/legacy/event/fast09/tech/full_papers/plank/plank_html/ ZFEC 应该更快。 https://gitlab.com/zfec/go-zfec中的一个实现似乎基于https://pypi.python.org/pypi/zfec。
ECC 在压缩后应用,并且通常在数据文件中交错,因为如果数据通过不可靠或嘈杂的通信通道传输,分布它们会使它们更加健壮。
在 Usenet 二进制组中,他们使用包含 ECC 信息的单独文件(参见 https://en.wikipedia.org/wiki/Parchive)。 这只会在存储库布局中添加另一个子目录,并将 ECC 应用于存储库管理信息(配置、索引等)也很容易。 但我不确定这样做是否会削弱 ECC 模式(也许 ECC 信息中对集群错误的鲁棒性会降低)。
感谢您的提示。 我在这里找到了该论文的 PDF 版本: https ://www.usenix.org/legacy/event/fast09/tech/full_papers/plank/plank.pdf
ZFEC Go 实现只是 C 库的一个包装器。
对于 ZFEC,在 [https://github.com/korvus81/jfec] 上有一个名为 jfec 的添加(使用 goroutine)的 Go 端口。
我添加了一个“项目”(最近添加到 GitHub)来跟踪新存储库格式的实现: https ://github.com/restic/restic/projects/3
打破向后兼容性时可以考虑的一些想法:
使用 sha512(或 sha512/256)代替 sha256 会提高备份速度吗? 据我所知,这对于除 ARM 之外的大多数平台都是如此。
Syncthing 讨论(https://github.com/syncthing/syncthing/issues/582)
博格讨论(https://github.com/jborg/attic/issues/209)
关于 sha512/256 的论文 (https://eprint.iacr.org/2010/548.pdf)
目前,每个有权写入存储库的人都有权读取它。 公钥加密将消除这一点,并且仍然允许基于散列的重复数据删除。
将公钥加密应用于数据 blob 会起作用,但我对 restic 如何处理树结构以了解它是否也可以成功实现这一点还不够熟悉。 它可能会引入很多复杂性。 如果只隐藏数据块,树中仍然有很多信息。
NaCl - https://godoc.org/golang.org/x/crypto/nacl/box
当前,当您偶然发现存储库时,无法知道您正在查看静态存储库。 我们目前正在密钥文件中泄漏"created":"TIMESTAMP","username":"XXXXX","hostname":"XXXXX"
。 我建议隐藏此信息,而是包含一些有关 restic 的信息,例如restic repository version X
。 可能就像 README 一样简单。
关于之前的讨论; 我非常赞成实施某种形式的纠错。
@oysols感谢您添加想法!
我将在下面添加我的想法:
从 sha256 更改为 sha512(为了速度)
目前,我并不关心速度(restic 已经非常快了),所以至少对我来说这个项目是低优先级的。 我们甚至可以简单地切换到支持 SIMD 的处理器的 SHA256 优化版本。 另一方面,当我们决定加快 restic 并且要讨论哈希时,我可能更喜欢 Keccak (SHA3) 或 Blake2,它们是(据我所知,我还没有做任何基准测试)快多了。
所以,从我的角度来看,这个项目现在被推迟了。
使用公钥加密而不是简单的密码
这个特性是计划好的(见#187),但是很复杂,需要大量的思考和几个主要的基础设施变化。 我也想推迟这一点,而是进行较小的增量更新,而不是我们更改所有内容 -> 推迟的更新。
存储库标识(将 README 文件添加到存储库中)
很好的一点,我们现在甚至可以在不破坏任何东西的情况下添加它。
存储库“信息泄漏”(从密钥文件中删除用户名、主机名和创建的时间戳)
这也是一个好点。 我们目前仅使用此信息将其与key list
命令中的密钥 ID 一起显示。 我们可以轻松删除username
和host
,时间戳不会提供太多信息,在大多数情况下它将与文件创建日期相同。
我想删除username
和host
并留下创建的时间戳。想法?
我今天玩过https://github.com/klauspost/reedsolomon ,我认为我们可以很容易地将纠错代码添加到包文件的末尾(一旦我们将包头移动到文件的开头)。 但是有两个缺点:
想法?
那么数据棒保护是可选的吗? 我认为尺寸的增加超过了边际(尽管我相信这对其他人来说是一个很棒的功能!)
让我稍微玩一下,这样我就可以了解当 ECC 与压缩相结合时,repo 会变大(或变小?)。 也许我们添加两种代码:一种用于包头,另一种(可能是可选的)用于数据。
删除用户名和主机
听起来是一个好主意。 如果我们想保留信息,可以将其添加到单独的加密字段中,就像主密钥一样。
ECC:文件大小将增加~14-30%,
我认为将 ECC 包含在包文件本身中并不是一个好主意。 它们在典型的还原方案中没有用,仅在包文件损坏的情况下使用。
我建议将奇偶校验数据放在单独的目录中:
repo/data/1e/1ef7267...
repo/parity/1e/1ef7267...
不管它是如何实现的; 通过多层压缩和加密,我认为某种 ECC 是必要的。 一个错误的位可能会引起很多麻烦。
感谢您的评论,让我们将讨论转移到我刚刚创建的单独问题:#804。
我不禁有这样的印象,有两个小组互相谈论restic中的前向纠错码。 一个小组想要(只是)保护 repo 免受 bitrot 的影响,因为即使是单个 bitflip 也会在重复数据删除的 repo 中产生巨大的问题。 另一组希望使用纠删码将 repo 分散到多个故障域(例如非 RAID 磁盘)。 Reed-Solomon 代码可以实现这两个目标,但它们需要不同的参数和不同的存储布局。
我使用我的 python 脚本 (https://github.com/oysols/restic-python) 对我的 repo 进行了快速检查。
header_length: 8688549
tree_length: 53898054
data_length: 146443506727
treeblobs: 8466
datablobs: 200975
packfiles: 29351
---- repo size by dir ----
155 config
146 510 470 574 data
27 538 629 index
4 545 keys
4 243 locks
14 041 snapshots
4 096 tmp
-----
Currently 116071 original files contained in the backup.
在 146 GB 的备份中,树 blob 只有 54 MB,当我们实施压缩时,它们可以很好地压缩到原始空间的三分之一左右。
将树 blob 与数据 blob 分开是否会提高性能?
似乎在还原期间执行的大多数操作都是在实际还原数据之前基于树 blob 完成的。 将它们分离为单独的包文件可以最大限度地减少解析备份树所需下载的数据量。 考虑到树 blob 的小尺寸,在开始恢复过程之前下载所有树 blob 可能会更快。
当然; 对于所有存储库,此分布可能并不相同。
您认为这值得进一步研究吗?
将树 blob 与数据 blob 分开是否会提高性能?
也许,这是我为未来考虑的优化之一。
除此之外,我还想为元数据添加一个本地缓存,这样就根本不需要从 repo 中获取它。 这应该会大大提高许多操作的速度。
将树 blob 与数据 blob 分开是否会提高性能?
从理论上讲,这可以改进prune
操作,因为如果树 blob 和数据 blob 位于单独的包文件中,则需要更少的重新打包(可能会批量删除旧的包文件而不是重新打包它)。
我已经在 #842 期间看到了
gcm 与 ctr: http ://www.daemonology.net/blog/2009-06-11-cryptographic-right-answers.html
sym vs asym:这个想法是对“会话”密钥进行公钥加密,对吗?
我们不要在这个问题上讨论加密,因为它现在被推迟了。 非对称加密讨论的相关问题是#187。 此外,在我们确定用例之前,我想保持高水平的讨论。 然后我们可以谈谈低级加密。
从密钥文件中删除用户名和主机名。
大量元数据泄漏!
例如, "username":"WorldBank\\JimYongKim"
明确表示高级所有者。
等待它被_removed_(或_encrypted_),因为第一个 Windows 二进制文件是在 2017 年 1 月编译的。
幸运的是,我在将 Restic 上传或推荐给有隐私意识的人之前检查了备份。
编辑:用户的时区也以纯文本形式提及,这也违反了保密原则。
回复:SHA3 - 这是关于为什么不值得采用的一种观点(还没有?): https ://www.imperialviolet.org/2017/05/31/skipsha3.html
因此,我认为可能不应该使用 SHA-3。 与 SHA-2 相比,它没有任何引人注目的优势,并且会带来很多成本。 我可以相信的唯一论点是拥有一个备份散列函数很好,但是 SHA-256 和 SHA-512 都受到普遍支持并且具有不同的内核。 所以我们已经部署了两个安全哈希函数,我认为我们不需要另一个。
我读过这篇文章,我理解 agl 的论点。 对于restic,这不是那么相关:我们使用散列函数来(唯一地)识别blob,而不是作为加密协议的构建块。 我查看其他哈希函数的想法主要是 SHA-256 计算速度慢,尤其是在低端系统上。 其他散列函数要快得多(例如 blake2)。
不确定这是否是回购格式的东西:如何使加密成为可选? 我正在考虑将存储在已加密磁盘的受信任备份服务器上的备份。
@mschiff有关该讨论,请参见#1018。 ;)
如何选择零件尺寸?
目前我每个文件有 4-6 MB。 文件更少但更大,远程备份会更快。
@fd0写道:
目前,我并不关心速度(restic 已经非常快了),所以至少对我来说这个项目是低优先级的。 我们甚至可以简单地切换到支持 SIMD 的处理器的 SHA256 优化版本。 另一方面,当我们决定加快 restic 并且要讨论哈希时,我可能更喜欢 Keccak (SHA3) 或 Blake2,它们是(据我所知,我还没有做任何基准测试)快多了。
另一个更快、更少 CPU 密集型哈希算法(如 Blake2)的考虑因素是在未连接电源的情况下进行备份时减少笔记本电脑的电池使用量。
回复第一个帖子:
从密钥文件中删除用户名和主机名。
这会被键名或某种描述取代吗? 我相信某种区分不同密钥的方法(无需访问密钥本身,例如在撤销某些机器的访问权限时)对使密钥管理有用吗?
一个新建议:对 blob、树和快照使用不同的键怎么样? AFAICS 将启用在备份存储服务器上而不是在客户端上发生遗忘和修剪的场景。 通过让存储服务器访问树和快照对象,它应该有足够的信息来确定哪些快照需要哪些对象以及哪些对象不再使用。 如果存储服务器都受到威胁,则可以访问树元数据,但不能访问实际文件内容。
通过只允许访问由树引用的对象 ID 列表,而不允许访问其余元数据(但这将需要为每棵树提供额外的数据结构),这可以稍微加强一点。
如果上述情况成为可能,这将为使用只写/只追加类型的存储开辟道路(备份的客户端无法删除备份,请参阅#784),而不必牺牲自动修剪,或数据安全。
关于我之前的评论(无需完全访问数据即可进行修剪):这也适用于(可能更强大)检查备份。 出于效率原因检查存储服务器上的存储库是有意义的(AFAICS 远程检查存储库需要传输所有内容),或者在实现真正的只写支持时(参见 https://github.com/ncw/rclone/issues /2499)。
此外,对于真正的只写方法,需要进行更改以限制它需要读取的文件(根据 https://github.com/ncw/rclone/issues/2499#issuecomment-418609301)。 我不确定这是否也需要更改存储库格式,如果需要,将这些包含在此处可能有用吗?
检查和修剪远程服务器上的存储库真的很棒。 我正在设置 restic 以备份多个主机,并希望在远程完成所有维护任务,以便客户端设置尽可能简单,只需要定期运行备份。
我想讨论快照文件格式的一些(可能是可选的)添加:
关于包文件格式我想问一下为什么不完全删除标题。
该信息冗余地包含在索引文件中。 已经有一些关于为纠错添加冗余的讨论,但是 IMO 应该(并且可以)将其与一般的 repo 格式分开,并且可以在此基础上添加或不添加。
简而言之:如果您不需要或不想要额外的信息来纠正错误,则无需在包头和索引文件中复制信息。 索引文件是高性能操作所必需的,并且在任何地方都可以使用。 很少使用包标头 - 如果那时仅用于双重检查或纠错。
另一个建议:将配置文件的用户名、主机名和内容添加到密钥文件的data
部分。 因此完全删除配置文件。
如前所述,用户名和主机应仅以加密形式存在。 要检查 KDF 派生的密钥是否有效,检查加密的data
部分的 MAC 就足够了。
IMO 将识别密钥所需的所有信息放在那里是有意义的。 添加存储在config
文件 ATM 中的信息只会从 repo 中删除一个额外的文件,并使 repo 初始化更容易。
很抱歉这个“愚蠢”的问题,但是否有任何认真的努力会在短期内引入改进的格式? 我多年来一直关注这个问题。 restic 目前不适用于大型数据集,或者当有很多快照时,它需要大量内存。 似乎缺乏压缩和 JSON 编码元数据的大量开销是 restic 的大问题。 也许努力已经停滞,因为人们认为需要实现“完美”?
我也对未来带给restic的东西感兴趣。 特别是在非对称加密和压缩中。
顺便说一句,对于一个新的哈希函数,还有 blake3,它非常新而且速度也非常快: https ://github.com/BLAKE3-team/BLAKE3
如果尚未就哈希函数的更改做出任何决定,这可能会很有趣。
repo.v2 的更多想法:
这应该会简化对“冷”存储的支持,下载速度非常慢或下载成本很高,比如亚马逊冰川。
* save tree and data blobs to different directories (in the past tree and data was mixed, but this was deprecated with introduction of cache).
我不喜欢这个想法。它使猜测备份的文件大小变得更加容易,而我看不到它的好处。
* add 'created time' info to data blobs.
我看不到添加“创建时间”的任何用途。 能给个用例吗?
这应该会简化对“冷”存储的支持,下载速度非常慢或下载成本很高,比如亚马逊冰川。
我想说“冷存储”支持已经可以通过当前的 repo 格式实现,方法是为 restic 添加一些微调的可能性,并双重存储不经常使用的文件,例如在本地缓存中。 另请参阅#2504
* save tree and data blobs to different directories (in the past tree and data was mixed, but this was deprecated with introduction of cache).
我不喜欢这个想法。它使猜测备份的文件大小变得更加容易,而我看不到它的好处。
早期关于这个问题的评论很好地阐述了好处:
https://github.com/restic/restic/issues/628#issuecomment -280436633
https://github.com/restic/restic/issues/628#issuecomment -280833405
第一条评论中的结果还表明,混合这两种类型的 blob 不会以任何有意义的方式模糊文件大小。
相关论坛帖子:
https://forum.restic.net/t/feature-using-an-index-for-restic-find/1773
@cfbao您所指的评论是关于在一个数据文件(包)中混合树和数据块。 分开这对于启用缓存处理很有用。 这在restic中也已经改变了。
但是,我仍然看不到将树和数据 blob 保存在不同目录中的任何好处。 能给个用例吗? (海事组织查找论坛主题不相关 - 我会在那里回答你)
但是,在单独的索引文件(例如目录“index-data”和“index-tree”)中分离树和数据 blob 条目将允许一些改进。
树 blob 已经存储在单独的包文件中(这是与缓存一起引入的)。
只需将它们写入不同的目录,就可以改善对下载速度非常慢的存储的支持(Amazon Glacier 标准需要 3-5 小时)。 就像存储所有元数据(常规 S3 中的索引+快照+树,以及 Glacier 中的数据)。
我更喜欢有一些反向代理的想法,它将tree+index+snapshots
存储在常规 S3 或任何其他地方,但将数据放入深度存档。
无论如何,当然可以使用restic
,但有一些限制。 但是一些格式更改可能会改善/简化事情。
@cfbao据我从代码中看到的restic find
不会从中受益。 它已经遍历了快照。 基本上它与restic ls <all-snapshots> | grep something
基本相同。
@dionorgua
我喜欢添加任意存储库作为“附加”缓存的想法,除了数据包之外的所有缓存都被缓存。 这不需要更改存储库布局,而且 IMO 更加灵活。
我已经在做这个了,另见#2516,最后的评论。
也许这是一个愚蠢的想法,但是与 borg 或 kopia 兼容的格式呢?
@aawsome
您所指的评论是关于在一个数据文件(包)中混合树和数据块。
真的。 我的错。 是的,这是我唯一关心的事情。
这在restic中也已经改变了。
你知道这是在哪个 PR/版本中更改的吗? 上次我检查我的 repo 时,我注意到同一个包文件中混合了数据和树 blob。 有什么办法可以(可能是慢慢地)转换我的回购以更好地分离?
我仍然看不到将树和数据 blob 保存在不同目录中的任何好处。 能给个用例吗?
我不知道。 如前所述,我真的不在乎这个。
@dionorgua
据我从代码中看到的,restic find 不会从中受益。 它已经遍历了快照。 基本上它与 restic ls 基本相同
| grep 的东西。
将树 blob 与数据 blob 分开不会减少查找所需的 API 调用次数吗? 如果集中,包含树 blob 的包文件的数量会减少,restic 可以下载较少数量的整个文件,而不是从许多包文件中获取许多段。 这对于速度相对较慢且速率限制更严格的后端(例如 Google Drive)很重要。
我可以(可能会慢慢地)以任何方式将我的回购转换为更好的
分离?
使用最近版本的 restic 运行“修剪”应该重建这些混合包..
请注意,“修剪”的实际实现会为远程存储库产生大量流量。 通过 #2718 中的实验性重新实现,您将只能重新打包混合包,同时具有最少的流量。
将树 blob 与数据 blob 分开不会减少 API 的数量
查找所需的电话?
在最近的版本和没有混合包的 repo 中,所有需要的信息都在本地缓存。
改进存储库格式的另一个想法:
正如我们所见,按 blob 类型分隔包文件是有益的(树和数据 blob 进入不同的包文件)。 按 blob 类型分隔索引文件不是一个好主意吗? 最近的索引 PR 已经朝着在内存表示中分离树和数据 blob 的索引条目的方向发展。
也有可能的优化只加载索引的一部分
在存储库中也这样做将允许更紧凑的表示,例如
{
"supersedes": [
"ed54ae36197f4745ebc4b54d10e0f623eaaaedd03013eb7ae90df881b7781452"
],
"type": "data",
"packs": [
{
"id": "73d04e6125cf3c28a299cc2f3cca3b78ceac396e4fcf9575e34536b26782413c",
"blobs": [
{
"id": "3ec79977ef0cf5de7b08cd12b874cd0f62bbaf7f07f3497a5b1bbcc8cb39b1ce",
"offset": 0,
"length": 25
},{
"id": "9ccb846e60d90d4eb915848add7aa7ea1e4bbabfc60e573db9f7bfb2789afbae",
"offset": 38,
"length": 100
},
{
"id": "d3dc577b4ffd38cc4b32122cabf8655a0223ed22edfd93b353dc0c3f2b0fdf66",
"offset": 150,
"length": 123
}
]
}, [...]
]
}
最有用的评论
是:包标头描述了包中的内容,这告诉了提取过程预期的内容(根据压缩算法、未压缩大小以及后来的其他属性,例如用于加密的密钥)。 同样需要在索引中表示,已引入该索引以便 restic 不需要查找包标头中的每个 blob。 所以同样的信息需要在那里出现。
我不喜欢这个想法,它使文件格式更加复杂:我们将在两个不同的地方拥有控制信息:在 blob 的开头和在标题中。 标头正是包含控制信息的位置。
原则上我同意,但文件系统是非常复杂的东西,并且错误传播(例如介质的读/写错误)通常不是最佳的。 对于高度减少(在冗余方面,例如去重)备份数据,我仍然认为添加(或提议添加)另一层纠错是个好主意。