1. Trie/Radix树
Trie树,又称 前缀树或字典树 ,是一种有序树,用于保存关联数组. 其中的键通常是字符串 。与二叉查找树不同, 键不是直接保存在节点中,而是由节点在树中的位置决定
。一个节点的所有子孙都有相同的前缀,也就是这个节点对应的字符串,而 根节点对应空字符串 。
一般情况下,不是所有的节点都有对应的值, 只有叶子节点和部分内部节点所对应的键才有相关的值 。 实际上trie每个节点是一个 确定长度的数组 ,数组中每个节点的值是一个指向子节点的指针,最后有个标志域, 标识这个位置为止是否是一个完整的字符串.
常见的用来存英文单词的trie每个节点是一个 长度为27的指针数组,index0-25代表a-z字符,26为标志域 。如图:
2. Patricia 帕特里夏树
Patricia树,或称Patricia trie,或crit bit tree,压缩前缀树,是一种更节省空间的Trie。对于基数树的每个节点,如果 该节点是唯一的儿子的话,就和父节点合并 。
3. Merkle 默克尔树
- MT是一种树,大多数是 二叉树 ,也可以多叉树,无论是几叉树,它都具有树结构的所有特点;
- Merkle Tree的 叶子节点的value是数据集合的单元数据或者单元数据HASH 。
- 非叶子节点的value是根据它下面所有的叶子节点值 ,然后按照Hash算法计算而得出的。
4. MPT(Merkle Patricia Trees)
知道了Merkle Tree,知道了Patricia Tree,顾名思义,MPT(Merkle Patricia Tree)就是这两者混合后的产物。
在以太坊中, 对于交易树来说,二叉Merkle Tree是非常好的数据结构。因为一旦树已经建立,花多少时间来编辑这棵树并不重要,它就会 永远存在并且不会改变 。
但是,对于状态树,情况会更复杂些。
- 以太坊中的状态树基本上包含了一个 键值映射 ,其中的 键是地址 ,而值包括账户的 声明、余额、随机数nonce、代码以及每一个账户的存储 (其中存储本身就是一颗树)。
- 状态树需要经常地进行更新:
- 账户余额和账户的随机数nonce经常会更变
- 新的账户会频繁地插入,存储的键( key)也会经常被插入以及删除。
我们需要这样的数据结构:
- 它能 在一次插入、更新、删除操作后快速计算到树根 ,而不需要重新计算整个树的Hash。
- 树的 深度是有限制 的,即使考虑攻击者会故意地制造一些交易,使得这颗树尽可能地深。不然,攻击者可以通过操纵树的深度,执行拒绝服务攻击(DOS attack),使得更新变得极其缓慢。
- 树的根只取决于数据 ,和其中的更新顺序无关。换个顺序进行更新,甚至重新从头计算树,并不会改变根。
MPT是最接近同时满足上面的性质的的数据结构。MPT的工作原理的最简单的解释是:
- 值通过键来存储
- 键被编码到搜索树必须要经过的路径中
- 每个节点有 16个孩子 ,因此路径由16进制的编码决定:例如,键‘dog’的16进制编码是6 4 6 15 6 7,所以从root开始到第六个分支,然后到第四个,再到第六个,再到第十五个,这样依次进行到达树的叶子。
- 当树稀少时需要一些额外的优化。