一、数据查找相关定义
数据查找是根据查询要求从一个计算机文件或数据库中提取所需要的数据的技术。如果要查找的数据全部放在计算机内存储器中,这种查找即称为内查找;若要查找的数据不在内存而在外存储器中,这种查找便称为外查找。
数据一般按照数据项、记录、文件三级组织在一定的结构之中。用于组织文件的基本数据项称为关键字。所谓从文件中查找数据是指根据给定的关键字值在文件中找出包含该关键字值的记录。对于不同的文件结构和查询要求,需要用不同的查找技术。
在各种数据集合(包括数组、线性表、树等数据结构)中,数据记录在集合中的实际位置是随机的,和数据记录的关键字之间不存在确定的关系。因此在数据集合中查找数据记录时需要进行一系列关键字的比较。比如在顺序查找时,比较的结果为“=”与“!= "两种可能。在折半查找、二叉排序树查找和树查找时,比较的结果为“>"、“<“ 与“=“ 三种可能。
查找算法大多数是建立在关键字比较的基础上。查找的效率主要决定于查找过程中所进行的比较次数和时长。在同等关键字模式下,以比较次数为最主要的因素。(比较树)
最理想的查找算法是不经过任何比较,一次操作便能得到所查的数据记录。那就必须在数据记录的实际位置和它的关键字之间建立一个唯一确定的对应关系,使每个关键字和数据集合中某个唯一的实际位置相对应。
数据查找三种实现方式:
(1)比较树,需要多次关键字的比较,才能找到数值;
(2)哈希表:数据记录位置与关键字奖励了对应关系。一次计算就能找到数值,需要一次关键字比较确认找到的是正确的关键字;
(3)前缀树:只需要一次关键字的比较,将关键字拆分成多个部分,分别进行比较;
二、比较树
算法的比较树(也被叫作决定树或查找树),获得的方法是,追踪算法行为,用树节点(我们用画圈来表示)来描绘每次键(关键字、key)比较。圈里我们放置用来与目标键做比较的那些键的索引。树枝(线)由圈的位置向下画,表示比较的结果,然后像前边一样被标记上。
比较树,一般使用的都是数字,不是字符串。将整个key进行原子化比较。
2.1 BST
二叉搜索树(BST)又称二叉查找树或二叉排序树。
(1)一棵二叉搜索树是以二叉树来组织的,可以使用一个链表数据结构来表示,其中每一个结点就是一个对象。一般地,除了key和位置数据之外,每个结点还包含属性lchild、rchild和parent,分别指向结点的左孩子、右孩子和双亲(父结点)。
(2)如果某个孩子结点或父结点不存在,则相应属性的值为空(NIL)。根结点是树中唯一父指针为NULL的结点,而叶子结点的孩子结点指针也为NULL。
BST的每个数据项都有各自的关键字(key),并以此与其他的数据项区分。查找某数据项就相当于查找其关键字。这样的访问方式有前提条件,即关键码要支持大小比较与相等比对。
2.2 AVL
AVL树是最先发明的自平衡二叉查找树。在AVL树中任何节点的两个子树的高度最大差别为1,所以它也被称为高度平衡树。AVL树本质上还是一棵二叉搜索树,它的特点是:
(1)本身首先是一棵二叉搜索树。
(2)带有平衡条件:每个结点的左右子树的高度之差的绝对值(平衡因子)最多为1。
也就是说,AVL树,本质上是带了平衡功能的二叉查找树(二叉排序树,二叉搜索树)。通过左旋和右旋算法调整树结构。
2.3 红黑树
红黑树(Red Black Tree) 是一种自平衡二叉查找树。在进行插入和删除操作时通过特定操作保持二叉查找树的平衡,从而获得较高的查找性能。
红黑树是一种平衡二叉查找树的变体,它的左右子树高差有可能大于 1,所以红黑树不是严格意义上的平衡二叉树(AVL),但进行平衡的代价较低, 其平均统计性能要强于 AVL。
红黑树应用于C++ STL 中 容器 map,set底层以及linux任务调度、虚拟内存等。
红黑树是近似平衡的二叉查找树,它含有以下特性:
(1)每个节点要么是红色,要么是黑色;
(2)根节点必须是黑色;
(3)红色节点不能连续(也即是,红色节点的孩子和父亲都不能是红色);
(4)对于每个节点,从该点至null(树尾端)的任何路径,都含有相同个数的黑色节点;
(5)每个叶子节点都为黑色;
2.4 跳表
增加了向前指针的链表叫作跳表。跳表全称叫做跳跃表,简称跳表。
跳表是一个随机化的数据结构,实质就是一种可以进行二分查找的有序链表。跳表在原有的有序链表上面增加了多级索引,通过索引来实现快速查找。跳表不仅能提高搜索性能,同时也可以提高插入和删除操作的性能。Rocksdb 的 memTable 使用内联跳跃表(Inline Skip List).
跳表的特性:
(1)最底层包含所有的元素;
(2)每个元素插入时随机生成它的level;
(3)如果一个元素出现在level(x),那么它一定出现在x以下的level中;
(4)每个索引节点有两个指针,一个向下,一个向右;
(5)跳表查询、插入、删除的时间复杂度为O(log n),与平衡二叉树接近;
(6)范围遍历性能高
跳表插入、删除、查找元素的时间复杂度跟红黑树都是一样量级的,时间复杂度都是O(logn),而且跳表有一个特性是红黑树无法匹敌的---范围扫描。
所以在工业中,跳表也会经常被用到。由于最大高度是初始时确定的,也就是说,索引的数据量需要提前估算出来。
2.5 B-树
B-树是一种多路搜索树。1970年,R.Bayer和E.mccreight提出了一种适用于 外查找 的树,它是一种平衡的多叉树,称为B树(或B-树、B_树)。
一棵m阶B树(balanced tree of order m)是一棵平衡的m路搜索树。它或者是空树,或者是满足下列性质的树:
(1)根结点至少有两个子女;
(2)每个非根节点所包含的关键字个数 j 满足:┌m/2┐ - 1 <= j <= m - 1;
(3)除根结点以外的所有结点(不包括叶子结点)的度数正好是关键字总数加1,故内部子树个数 k 满足:┌m/2┐ <= k <= m ;
(4)所有的叶子结点都位于同一层。
B-树的搜索,从根结点开始,对结点内的关键字(有序)序列进行二分查找,如果命中则结束,否则进入查询关键字所属范围的儿子结点;重复,直到所对应的孩子指针为空,或已经是叶子结点
2.6 B+树
B+ 树是一种树数据结构,是一个n叉树,每个节点通常有多个孩子,一棵B+树包含根节点、内部节点和叶子节点。根节点可能是一个叶子节点,也可能是一个包含两个或两个以上孩子节点的节点。
B+树是应文件系统所需而出的一种B-树的变型树。B+树是为磁盘或其他直接存取辅助设备而设计的一种平衡查找树。B+树是B-树的变体,也是一种多路搜索树:
B+树定义基本与B-树同,除了:
(1)非叶子结点的子树指针与关键字个数相同;
(2)非叶子结点的子树指针P[i],指向关键字值属于[K[i], K[i+1])的子树(B-树是开区间);
(3)为所有叶子结点增加一个链指针;
(4)所有关键字都在叶子结点出现;
B+ 树通常用于数据库和操作系统的文件系统中。NTFS, NSS, XFS, JFS和BFS等文件系统都在使用B+树作为元数据索引。B+ 树的特点是能够保持数据稳定有序,其插入与修改拥有较稳定的对数时间复杂度。B+ 树元素自底向上插入。
三、哈希表
散列表(Hash table,也叫哈希表),是根据关键字(Key)而直接进行访问的数据结构。通过把关键字映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。
给定表M,存在函数f(key),对任意给定的关键字key,代入函数后若能得到包含该关键字的记录在表中的地址,则称表M为哈希(Hash)表,函数f(key)为哈希(Hash) 函数。
Linux内核中大量使用了hash表。
如果两个不同关键字的hash Code相同(在表中的地址),这种现象称为hash冲突。任意长度的消息压缩到固定长度的消息时的信息丢失,是导致hash冲突的根本原因。
哈希表内解决哈希冲突的方法:
(1)开发定址法(线性探测再散列,二次探测再散列,伪随机探测再散列):根据当前地址再计算出一个地址
(2)再哈希法:同时构造多个不同的哈希函数
(3)链地址法:所有哈希地址相同的放在同一个链表中
(4)建立一个公共溢出区:哈希表分为基本表和溢出表两部分,凡是和基本表发生冲突的元素,一律填入溢出表
哈希冲突过多,意味着:数据insert/delete的时候需要lock整个冲突链表,同步开销增大。冲突链表过长,造成遍历开销过大。解决冲突的有效办法,就是用更大的hash表,对数据进行rehash。
四、前缀树
trie,又称前缀树或字典树,是一种有序树,用于保存关联数组,其中的键通常是字符串。与二叉查找树不同,键不是直接保存在节点中,而是由节点在树中的位置决定。一个节点的所有子孙都有相同的前缀,也就是这个节点对应的字符串,而根节点对应空字符串。一般情况下,不是所有的节点都有对应的值,只有叶子节点和部分内部节点所对应的键才有相关的值。
它有3个基本性质:
(1)根节点不包含字符,除根节点外每一个节点都只包含一个字符;
(2)从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的关键字;
(3)每个节点的所有子节点包含的字符都不相同。
4.1 基数树
基数树(Radix tree),又称压缩前缀树,是一种更节省空间的Trie(前缀树)。所有节点使用固定大小的数组,用于存储 token 的所有值。以二进制位串为关键字的 trie 树,是一种多叉树形结构,同时又类似多层索引表,每个中间节点包含指向多个子节点的指针数组,叶子节点包含指向实际的对象的指针。
其特性是:
(1)树的高度(复杂度)与记录数量无关,仅与 key 值长度有关
(2)更新和删除不涉及到树结构的调整,不同插入顺序产生相同的树
(3)到达叶子节点的路径,表示该节点的 key。
缺点: 在 key 值稀疏情况下,每个 node 内的空间占用很少,导致内存上的浪费,所以 Radix Tree 并没有广泛应用在 DBMS 中。
4.2 ART树
Adaptive Radix Tree(自适应基数/前缀树,ART)可变的基数树,在 Radix tree 的基础上,优化增加可变特性,其核心是优化空间的利用率。每个节点的空间大小不再相同,而是根据需要使用不同的节点类型。
ART树针对基数树的优化点有: 节点划分不同类型、路径压缩、懒扩展。
五、MassTree
可以理解为B+ Tree 和 Radix Tree 的混合体,即将键切分成多个部分,每个部分为一个节点;每个节点内部又是一个 B+ Tree,兼顾空间和性能。
每个虚线框代表一棵 B+ 树,对于每一层(Layer)的 B+ 树,采用8字节进行索引。长度小于 (8h + 8) 字节的 key 会被存放在 layer <=h。比如,两个 key 存放在 layer 1,那么它们的前8字节是相同的。
圆形代表内部节点(interior node,也就是 B+ 树的 branch node),矩形代表边缘节点(border node,也就是 B+ 树的 leaf node),五角星代表 value。border node 的 value 域可能存放的是数据,也可能存放的是下一层子树的根节点。
六、哈希树
哈希树(hash tree、Merkle tree),是一种树形数据结构,每个叶节点均以数据块的哈希作为标签,而除了叶节点以外的节点则以其子节点标签的加密哈希作为标签 。哈希树能够高效、安全地验证大型数据结构的内容。
选择质数分辨算法来建立一棵哈希树。
Root为哈希树的根(深蓝色节点)。
R0和R1为第一层节点(灰色节点)共有2个,对应除数序列中的第一个数 。
R00、R01、R02、R10、R11、R12为第二层节点(黄色节点)共6个。其中R00、R01和R02是属于R0的子节点,R10、R11和R12是属于R1的子节点。R0和R1下分别有3个子节点,对应除数序列中的第二个数 。
后续的R00、R01、R02、R10、R11和R12节点下又分别有5个子节点(白色节点),对应除数序列中的第三个数 。
从2起的连续质数,连续10个质数就可以分辨大约M(10) =23571113171923*29= 6464693230
七、查找算法性能比较
ART :自适应基数树(The Adaptive Radix Tree)
CSB :一种内存优化后的B+树(Cache-Sensitive B +-tree [CSB]),
kary :每个节点都有k维点的树(k-ary search tree)
FAST : 对平衡二叉树查询进行了优化( Fast Architecture Sensitive Tree)
GPT :基数树 (Generalized Prefix Tree)
• RB :红黑树(red-black tree) ,
• HT :哈希表(chained hash table,using MurmurHash).
从上面两个比较中,可以发现 ART树拥有极好的性能,几乎可以匹敌哈希表。ART树比哈希表的优势是,支持范围查找。因此,云溪数据库在存储层使用ART树算法进行数据查找。