作者:Anony

在尝试运行比特币全节点时,许多人会在存储设备的选择上犯难。当前主流的存储设备可以分成两种:机械硬盘和固态硬盘。机械硬盘(HDD)更便宜;在需要存储大体积数据时,这种经济性优势非常显著。但固态硬盘(SSD)的性能更好。那么,前者依然是一种选择吗?有没有一些办法,能够既获得固态硬盘的一些好处,又不需要付出那么大的代价?

要回答这个问题,我们先要知道,比特币节点要存储哪些数据。

区块数据与状态数据

作为参与比特币网络的一台计算机,比特币全(验证)节点需要存储两类数据:一是区块数据;二是状态数据。

区块数据是比特币网络确认其发生过的所有交易的记录,它以 “区块链” 的结构存在:每个区块都指向自己的前一个区块,每个区块都包含一定数量的交易。而状态数据则是这些交易处理完成之后的结果 —— 每一笔比特币资金的大小以及使用什么样的锁定脚本。状态数据的形式是 “UTXO 集”。状态数据也是新区块验证能力的前提:需要从 UTXO 集中检索出交易所引用的 UTXO 输入的信息,才能验证交易的有效性。

区块数据只增不减,网络每挖出一个区块,就需要多占用一些空间。而状态数据则可能增减,虽然其整体是呈增加趋势的。截至本文撰写之时,比特币网络的区块数据在 560 GB 左右,而状态数据在 10 GB 左右(这部分数据在过去一年中因为一些糟糕设计的链外合约系统的采用而大大膨胀)。

显然,区块数据就是我们要使用大容量存储设备的原因 —— 如果我们要在本地保留所有的区块数据的话 —— 也是我们难以平衡经济性与性能的原因。

一种办法是运行 “剪枝模式(pruning mode)”。剪枝模式会自动删除掉一定时间点以前的区块,只保留最新的一定数量的区块(比如 5000 个区块)、控制区块存储的体积在一定限制(比如 2 GB)之下(这两个数值都允许用户自己配置)。缩减了区块存储之后,用户自然可以使用少量的固态硬盘空间来运行节点,甚至无需额外购置硬盘,只需使用自己现有的计算机设备。

剪枝模式保持了 “全验证” 的能力,因为它保存了完整状态数据。但它也有一些不便利,当用户需要 “重新扫描(rescan)” 区块链以了解一些历史交易的信息时(比如获取一个较早创建的钱包的历史交易),它可能无法支持这样的操作。

另一种办法是深入了解这两种数据的不同,以及何以固态硬盘能为我们提供性能上的提升。

区块数据是一种低读写频率的数据:当一个区块存储到硬盘中之后,回头重新访问它的机会是很少的,几乎只在你需要向对等节点提供这些历史区块时,以及重新扫描时,才会用到。越久远的区块就越是如此。而状态数据是高读写频率的数据,并且是随机读写:每当新区块到达时,我们都要检索状态数据以验证交易;而且,因为交易会访问哪些状态(UTXO)是无法预测的,所以它是一种随机的读取。

这也是固态硬盘能够提供更好性能的原因:其随机读写的速度比机械硬盘更高。

既然如此,我们就可以设想一种两全其美的方法:使用机械硬盘来存储区块数据,同时用固态硬盘来存储状态数据。这样做只需要 “大容量的机械硬盘 + 小容量的固态硬盘”,既获得了更好的性能,又不至于太贵。

问题是,该怎么做?

:到目前为止,普通用户可以使用市场上的机械硬盘新品来运行比特币节点(同时存储区块数据和状态数据),这是完全可以做到的。比特币节点的开销得到了很好的控制。但非常老的机械硬盘产品(比如 10 年以前的消费级机械硬盘)可能已无法满足性能要求。

此外,给定现在许多电脑都使用固态硬盘来作为存储操作系统的专用空间,或者甚至在普通数据的存储中也使用固态硬盘,闲置的空间就可以被我们这里所述的方法使用。性能提升效果是明显的。

软链接

在操作系统中,有一种叫做 “软链接(Symbolic Link)” 的工具,它的作用类似于 “快捷方式”:不管文件 A 存储在哪里、A 的快捷方式放在哪里,访问 A 的快捷方式都会跳转成访问 A。

具体来说,它可以为文件存储生成一个 “名义位置/目录”,但凡我们要读写这个名义位置,都会跳转到其实际位置进行读写,而不管这个实际位置在哪里。

在 Windows 系统中,创建软链接的方式是:使用管理员身份运行 “命令提示符”,并在命令行输入下列命令并回车运行:

mklink \d "名义目录" "真实存储位置"

而在基于 Linux 的系统中,创建软链接的方式是:在命令行界面输入下列命令并运行:

ln -s "真实存储位置" "名义目录"

自此,当软件要向名义目录 读取/写入 数据时,都会跳转到其真实存储位置来读写。

Bitcoin Core 软件的运行中,它会在用户配置的 datadir(数据目录)形成两个文件夹:blocksstate,分别对应上文所说的区块数据和状态数据。我们可以通过把它们设置为另一个真实存储位置的软链接,来规划数据的真实存储位置。例如(在 Windows 系统中):

mklink \d ".../blocks" ".../HDD/bitcoin-blocks"

它会把区块数据存放到后面这个目录下(指向机械硬盘的某个目录)。

:当一个文件夹(目录)下还没有任何文件时(比如你刚开始运行一个节点,还未下载任何区块数据),你可以任意将它做成一个软链接。但如果它里面已经有文件,请将其中的所有文件转移到你真正希望存储的位置,然后再将它设为软链接。

使用这种办法,你可以为新节点设置放在固态硬盘中的 datadir,然后将 blocks 设为软链接、指向机械硬盘的存储位置。也可以在你已经运行了一段时间、datadir 设置为机械硬盘目录的节点中,将 state 文件夹内的状态数据转移到固态硬盘,然后把 state 设为指向固态硬盘存储位置的软链接。

这种做法有许许多多好处:

  • 使用固态硬盘来存放需要频繁随机读写的状态数据,可以提高节点的性能。在 IBD(初始化区块下载;全节点初始化)的过程中,这种性能提升的效果尤为明显。
  • 在提高性能的同时,控制了经济成本。用户不再需要使用大容量的固态硬盘,而只需小容量的固态硬盘,或者在已经服役的固态硬盘中拨出一部分空间。
    • 小容量的固态硬盘比起大容量的,往往有更好的寿命。
  • 实际上也延长了机械硬盘的服役时间,或者说准许用户选择更老旧、性能更差的机械硬盘。因为频繁读写的任务实际上由固态硬盘承担了,而机械硬盘承担的是持久化保存数据的任务。

试试看吧!

本文所受的启发来自:https://bitcoin.stackexchange.com/questions/48617/how-to-speed-up-initial-block-chain-sync-using-ssd-without-wasting-space

(完)