mLSM:Making Authenticated Storage Faster in Ethereum(部分核心内容翻译)

Abstract

以太坊提供了可验证的存储,会返回value和proof,value是查询的存储内容,proof用来验证返回的value值得正确性。但是在实验运行过程中,发现了读写放大的问题(read and write amplification(64 * in the worst case)),作者提出了mLSM(Merkelized LSM)来减弱读写放大问题

Introduction

…(phase 1:介绍了以太坊的发展现状)
将以太坊作为一个存储系统,以便寻找提升读写性能的方式。以太坊提供了可验证性存储。读操作在以太坊中会返回一个value和一个用来验证value的Merkle Proof。每个读取过程中的key都是Merkle tree的一部分;树的内部节点只是它们的孩子哈希。value可以沿着proof逐层向上hash,从而验证value的正确性。每个写操作是需要从根节点到叶子结点,也就是说一旦改变某个叶子结点的值,就要逐层向上取哈希,一直到根节点。
采用了显示以太坊中的区块链数据,以太坊的存储才用了LevelDB的key-value存储,通过实验,一个单一key的读取过程可能导致LevelDB64倍的读取,写操作同样如此。这样的读写操作极大的影响了以太坊的数据吞吐量,同时写操作放大的问题也影响了固态硬盘的寿命。
讲抽象问题转化成具体问题,那就是:怎么才能设计一个最小化读写操作的可验证存储系统?所以为了克服挑战,我们提出了Merkle+LSM(Log-Structured Merge Tree)合并操作。The key insights behind mLSM are maintaining multiple independent trie and decoupling the two current uses of tries in Ethereum: lookup and authentication与LSM类似的是mLSM同样拥有多层level。==Each level contains a number of immutable merkle trees, with merkle trees becoming bigger with higher-numbered levels.==更新mLSM是将它们在内存中打包的过程,然后再Level0写入一个新的merkle树。
…(phase 4: 介绍了mLSM会对以太坊可验证存储的读写操作提升许多。)

Background

这一部分提供了区块链和以太坊的背景知识

Blockchain

balabala讲了区块链的工作原理

Merkle Tree

The root of the tree is publicly available to clients, and clients can use this to verify reads.
在这里插入图片描述

Ethereum protocol

以太坊节点之间的连接通过远程过程调度(RPC)来交换信息。区块头的组成是包含了本区块的hash root和上一个区块的区块头的hash散列以及一个唯一标识的码。以太坊采用Kec-256哈希加密方式加密。采用MPT树进行存储数据,包含了global state trie和收据树和交易树。
ETH and LES protocols在以太坊中存在着两种类型的节点----fullnode、fastnode和lightnode,fullnode包含了所有区块链的历史信息;lightnode仅仅包含了区块头,但是它可以通过远程过程调度(RPC)通过与fullnode交互,获得区块体。Ethereum run ETH protocol between 2 fullnode in the cluster, and LES protocol to manage the interaction between a lightnode and a fullnode in the cluster. A fullnode returns a merkle proof along with the requested data to a lightnode.

Storage in Ethereum

基于LSM的key-value存储有很多,比如LevelDB、RocksDB和PebbleDB,它们都提供了高写入和低读取的特点suo,它们擅长随机读和写。以太坊采用加密哈希作为它们数据的唯一标识码;所以采用了LevelDB作为存储介质,存储hash值作为key,存储数据作为value。以太坊使用RLP编码来加密数据。

Merkle Patricia Trie

balabala一些MPT介绍的材料,网上很多

LevelDB

举个例子,交易ID为key的话,经过RLP编码的交易就是value。

State trie

(讲了State trie的结构和组成)To improve performance, Ethereum uses a shortNode in the patricia trie to compact many nodes along a path into a single node. Even with this optimization, reading a single value in Ethereum requires tens of LevelDB reads.
(就此,文章阐述出了具体存在的问题)

The Ethereum Storage Bottleneck

以太坊可验证存储存在IO放大问题:每次以太坊读取操作需要大量的LevelDB读取;每次以太坊写入操作需要多层LevelDB写入。攻击者可以利用这点对以太坊发起攻击。
造成存储瓶颈的问题是可验证存储设计的问题。造成这个瓶颈的主要是原因是仅仅构造一个Patricia Trie在最顶层的LevelDB上。最坏的情况 get() 请求时,会导致64倍的放大问题。
在这里插入图片描述
Experimental setup 根据爬取得真实交易账户数据建立私有链,使用Geth并且开始fullNode。实验环境是:16gb内存,Intel 750系列2TB SSD,初始区块链大小为1.6M
Result 调用不同的web3 geth语句有不同的结果
- getBlock:getBlock(BlockNum)根据区块编号可以返回区块体(5 LevelDB扩展到8M)
- getTransaction:getTX(txHash)根据哈希值返回具体的交易细节(2 LevelDB扩展到10.4M),原因是这个交易的查询是对Local Transaction Trie,并不是全局状态树。
- getBalance:getBalance(addr)根据地址查询到当前账户的余额。(7 LevelDB扩展到1.4M),在这种情况下,最极端的情况可能到达64 LevelDB的放大。
轻型客户端 lightNode 同样的查询过程是lightNode发送请求到fullNode,fullNode同样也得承受来自轻型客户端的请求压力。
Latency metrics
在这里插入图片描述

Proposed solution: Merkle LSM

为了解决读操作过程中的存储放大问题,提出了mLSM的概念。

Caching merkle proofs

最主要的问题是以太坊读取操作会导致多层LevelDB进行读取。一个简单的解决方案是只缓存LevelDB中每个Ethereum读取的值和merkle证明,并使用缓存来提供以太坊读取对于一个只读操作是可行的;问题会出现在写操作上。每次写操作都会更新Patricia Trie中的几个节点,包括根节点。这就会导致所有被缓存的merkle根无效。整个缓存在每次写入时都会失效,这使得这种方法变得不切实际,并展示了经过身份验证的存储的独特挑战。

Merkelized LSM

Insight 采用简单的解决方法解决关键问题。这个简单的解决方案不起作用,

  • 因为==树中的任何两个节点之间存在紧密耦合(因为所有节点在单个根下形成单个树)==所以,mLSM致力于管理多个独立的tries,从而让某一颗trie的值的改变不会影响其他的trie。When a write happens, only the cached values of the affected tree have to be invalidated.(发生写入时,只有受影响树的缓存值必须无效。解释:这里点出了修改读操作的核心,就是解耦合查询和验证管理的过程)
  • 从微不足道的解决方案中获得的另一个关键见解是从trie本身中去除值的查找。 trie的主要目的是提供身份验证,但它不一定是查找的唯一来源。 解耦查找和身份验证可以减少查找期间LevelDB获取的数量。
    Design mLSM=Merkle Tree+LSM。简单的说mLSM就是用patricia trees代替ssTable。在mLSM中设计类似于LSM的多层结构,每层有多个持久化存储的merkle proof。为了让所有的patricia trie串联起来(根本目的是为了让读取是可以验证的),mLSM引入了master root节点的概念。每层的merkle proof并不包含在master root hash中;通过这种方式,我们确保了每一层中的每个树的独立性。mLSM采用binary Merkle tree代替了merkle patricia tries,从而更好的保证了数据的平衡性。
    在这里插入图片描述
    **mLSM Writes(重要)**与LSM类似,mLSM也是将写入更新先缓存到内存中,然后到达一定数量之后,再统一将其写入存储中。但是mLSM不同的是,它不是将更新写入,而是将其作为一个新的binary merkle tree写入Level 0当Level0到达了事先阈值,它将进入compacted过程(merged to form a larger binary merkle tree),然后将大批量I/O写入下一级别。需要注意的是,写入仅仅影响新的binary merkle tree和master root;存储系统的其它部分不会受到影响,解决了刚才Insight中提出的第一个要点(减小树之间的依赖)
    **mLSM Reads(重要)**与LSM的读取过程类似,mLSM的读取可能也要检查多个Level的数据,在Level 0层中,可能有多个tries包含有符合要求的key;在所有其它Level中,mLSM维持不变量,即包含所需密钥的单个trie。 一旦识别出正确的merkle二叉树,就会查询它以获得value和merkle证明。 然后扩展merkle证明以包括主节点,并且将值和merkle证明返回给用户。
    Caching mLSM缓存每个密钥的值和身份验证。 mLSM使用LevelDB进行缓存,密钥是原始密钥,值是数据和merkle证明的组合。 mLSM读取从最低级别开始逐个检查不同级别,并在级别中找到密钥后返回。 mLSM中的每个级别都有一个缓存,并且读取缓存的读取不需要遍历merkle树来读取值和merkle证明。 0级缓存需要使用版本号之类的东西来处理同一密钥的多个副本.
    Challenges请注意,由于多个级别,mLSM引入了自己的读写放大。 我们建议用Bloom Filters [5]处理读取放大。 Bloom过滤器可用于有效地优化查找,以便我们只读取可能包含密钥的trie。 通过这样的优化,平均而言,我们将只读取一个用于Get请求的trie,并且可以使用来自相应缓存的单个LevelDB获取来检索值和merkle证明。 mLSM结构引入的写入放大是另一个挑战。 我们可以借鉴我们之前的工作Fragmented LogStructured Merge Trees [23]中的想法,以减少写入放大,同时保持读取性能
    另一个微妙的挑战是,由于mLSM结构用于验证读数,因此不同的节点应该在任何时间点反映相同的mLSM状态。 这意味着尝试的压缩应该是确定性的,并且应该在所有节点中具有相同的行为 - 今天基于LSM的键值存储不是这种情况,其中压缩是在后台以非确定性方式触发的。 确定性压缩可能通过使用压缩操作来强制执行,这会强制压缩状态而不是触发阈值。 其他挑战包括有效地将bloom过滤器存储在内存中并限制0级尝试的次数。
    Cost Analysis当bloom过滤器添加到mLSM中的每个trie时,每次mLSM读取只需要以高概率读取一个trie。因此,在高速缓存未命中时,mLSM读取的成本将是O(D),其中D是密钥所在的merkle二叉树的深度。我们相信这将远远小于今天在以太坊中使用的merkle patricia trie的深度。对于密钥的所有连续读取(在高速缓存命中期间),读取的成本是O(1),因为它可以仅通过一次LevelDB查找来提供。
    每个mLSM写入将被缓冲在内存中,并作为不可变的merkle二进制树写入级别0.每个键只会写入每个级别一次,因此写入成本将为O(H),其中H是mLSM的高度。我们相信这也将远远低于今天以太坊的写入成本,后者必须更新从叶子到根的路径上的所有节点。另外,由于写入被缓冲在存储器中并作为批处理写入,因此写入成本在所有键中摊销。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值