分布式数据同步之Merkle Tree

这两天在看Dynamo数据库的数据同步机制,涉及到了一种数据结构,叫Merkle Tree,并在此基础上构建了一棵叫Merkle Hash Tree的数,下面简单总结一下。

什么是Merkel Tree?

Merkle Tree,是一种树(数据结构中所说的树)。而Merkle Hash Tree则是因为它所构造的Merkle Tree的所有节点都是Hash值。

Merkle Tree具有以下特点:

1. 它是一种树,可以是二叉树,也可以多叉树,无论是几叉树,它都具有树结构的所有特点,应用中一般以二叉树为主;

2. Merkle树的叶子节点上的value,是由你指定的,这主要看你的设计了,如Merkle Hash Tree会将数据(文件)的Hash值作为叶子节点的值;

3 非叶子节点的value是根据它下面所有的叶子节点值,然后按照一定的算法计算而得出的。如Merkle Hash Tree的非叶子节点value的计算方法是将该节点的所有子节点进行组合,然后对组合结果进行hash计算所得出的hash value。

例如,下图就是一个Merkle Hash Tree形状,如果它是Merkle Hash Tree,则节点7的hash value必须是通过节点15、16上的value计算而得到。piece x是对应的数据(文件)

哈希树的特点很鲜明: 叶子节点存储的是数据文件,而非叶子节点存储的是其子节点的哈希值(称为MessageDigest) 这些非叶子节点的Hash被称作路径哈希值, 叶子节点的Hash值是真实数据的Hash值.

               分布式数据同步之Merkle Tree_第1张图片

Merkle Hash Tree

为什么要使用Merkle Tree

目前, 在计算机领域,Merkle Tree大多用来进行比对以及验证处理。在处理比对或验证的应用场景中时,特别是在分布式环境下进行比对或验证时,Merkle Tree会大大减少数据的传输量以及计算的复杂度。

例如,就拿图一举例,假如是 15,16.......30是一个个数据块的hash值,我把这些数据从A传输到B,数据传输到B后,我想验证下传输到B上的数据的有效性型(验证数据是否在传输过程中发生变化),只需要验证A 和 B上所构造的Merkle Tree的root节点值是否一致即可,如果一致,表示数据是有效的,传输过程中没有发生改变。假如在传输过程中,15对应的数据被人篡改,通过Merkle Tree很容易定位找到(因为此时,节点0,1,3,7,15对应的hash值都发生了变化),定位的时间复杂度为O(log(n)).
哈希树经常应用在一些分布式系统或者分布式存储中的反熵机制(Anti-entropy),也有称做去熵的.这些应用包括 Amazon的Dynamo 还有Apache的Cassandra数据库, 通过去熵可以去做到各个不同节点的同步, 即保持各个节点的信息都是同步最新.

应用举例

BitTorrent中应用(验证)

在BT中, 通常种子文件中包含的信息是Root值, 此外还有文件长度、数据块长等重要信息. 当客户端下载数据块8时,在下载前,它将要求peer提供校验块8所需的全部路径哈希值:H24、H12、H6和H1. 下载完成后, 客户端就会开始校验, 它先计算它已经下载的数据块8的Hash值23, 记做H23′, 表示尚未验证. 随后会按照我在上一小节中给出的几个公式, 来依次求解 直到得到H0′并与H0做比较, 校验通过则下载无误. 校验通过的这些路径哈希值会被缓存下来, 当一定数量的路径哈希值被缓存之后,后继数据块的校验过程将被极大简化。此时我们可以直接利用校验路径上层次最低的已知路径哈希值来对数据块进行部分校验,而无需每次都校验至根哈希值H0.


下面,让我们来看看其数据块交换及校验的详细过程:

  1. 以下载某大文件F为例,为启动BT交换,每一个client均必须从索引服务器中下载F所相应的.torrent文件,从而得到文件长度、数据块长及根哈希值H0等必要信息;
  2. 任一client均可通过轮询Tracking服务器或者是DHT方式来定位其他参与交换F数据的peer,进而与之建立TCP连接进行P2P文件交换,其定位peer的方式基本上没有什么改变。
  3. 当client从某一peer下载一个数据块时,它将事先向peer发出相应的校验查询请求,要求对方提供校验当前下载块所需的路径哈希值,这一过程可以很方便地通过扩展标准的Peer Wire Protocol实现。以上图为例,当client下载的是数据块8时,在下载开始之前,它将要求peer提供校验块8所需的全部路径哈希值:H24、H12、H6和H1;
  4. 当client下载完某数据块之后,它将对其进行哈希树校验;仍以校验块8为例,client将首先计算出块8的哈希值H23',然后再根据peer所提供的信息依次计算各层路径哈希值,直至求得一个唯一的根校验值H0',如果H0'等于.torrent文件中的H0,则数据块下载无误,校验通过;反之则校验失败。
  5. 一旦某数据块校验通过,与其哈希树校验过程相关的所有路径哈希值均可以作为cache缓存下来,而不管其究竟是从peer查询所得的,还是由client自身计算所得的;但值得注意的是,如果该数据块的校验未通过,则其所有相关的路径哈希值均不能被cache,而只能被废弃,因为此时我们无法保证peer所提供的路径哈希值的正确性(不能排除peer故意提供错误路径哈希值和哈希值传输出错的可能)。例如,所有与块8校验相关的路径哈希包括:H24、H12、H6、H1、H11、H5、H2,其中前面4个经查询peer所得,而后面3个则由client自己计算所得。块8验证的结果将决定这7个路径哈希是否可信及是否能被cache。
  6. 当一定数量的路径哈希值被缓存之后,后继数据块的校验过程将被极大简化。此时我们可以直接利用校验路径上层次最低的已知路径哈希值来对数据块进行部分校验,而无需每次都校验至根哈希值H0,这主要是因为:某一路径哈希值被cache这一事实本身便能够证明其是可信的;例如,在块8校验完成之后,client无需进行额外的peer查询便可以直接对块9进行校验,因此它已经知道了H24的值,并且通过块8的校验过程验证了H24的正确性;而当client需要对块10进行校验时,它仅需向peer查询H26一个校验值即可,由于我们已经知道了H12值,因此对块10的校验仅需2次SHA1哈希即可:一次是计算块哈希H25,一次是计算SHA1(H25,H26),并比较其等不等于H12。同理,当需要验证块12时,由于已知H6,client仅需查询2个路径哈希(H28和H14)和3次SHA1计算(H27、SHA1(H27,H28)和SHA1(H13,H14))即可。缓存的路径哈希值越多,则后继数据块校验越容易,速度也越快。
  7. 为了进一步提高校验效率,可以考虑在.torrent文件上再做做文章:令其保存整个哈希树中最上面的若干层路径哈希值,而不仅仅只是一个根哈希值H0,底层的路径哈希值则仍然依靠询问peer或client自身计算所得,从而实现.torrent文件尺寸与校验效率的有效折衷。

Amazon Dynamo中同步(比对)

在Dynamo中,每个节点保存一个范围内的key值,不同节点间存在有相互交迭的key值范围。在去熵操作中,考虑的仅仅是某两个节点间共有的key值范围。MT的叶子节点即是这个共有的key值范围内每个key的hash,通过叶子节点的hash自底向上便可以构建出一颗MT。Dynamo首先比对MT根处的hash,如果一致则表示两者完全一致,否则将其子节点交换并继续比较的过程, 知道定位到有差异的数据块. 这种同步方式在分布式中有着节省网络传输量的优点.


总结

1、Merkle Tree是一棵多叉树(主要是二叉),主要用于在分布式环境中的比对以及数据验证作用,减少网络通信量以及计算开销。
2、BT应用中,主要用来做数据的验证,减小种子文件的大小。种子文件中保存了数据块的地址信息以及Merkle Tree的根节点Hash值,在P2P通信时,当收到一个数据块时,计算该数据块的HASH,并请求P2P对端返回与该数据块相关的路径哈希值,从而计算出根节点HASH值,与种子文件中的作比对,如果相等,则说明数据块正确,否则出错。
3、在数据同步应用中,主要用作数据副本间的数据比对,发现数据的差异。每个副本计算各自的HASH根节点,其中,N个数据块的HASH值作为叶子节点。在进行数据同步前,先传输根节点HASH值,若发现相等,则说明数据副本相等,不需要进行同步。若根节点HASH值不等,说明数据有异,此时需要找出相异的数据并进地同步:请求传输merkle tree中下一层的节点HASH值,按照HASH值不相等的分支一直走到叶子节点,最终就能定位出数据相异的数据块,定位复杂度为O(log n)。

你可能感兴趣的:(分布式存储)