索引的十种数据结构梳理汇总

1,Bloom

布隆过滤器是一个 bit 向量或者说 bit 数组,长这样:
在这里插入图片描述
 如果我们要映射一个值到布隆过滤器中,我们需要使用多个不同的哈希函数生成多个哈希值,并对每个生成的哈希值指向的 bit 位置 1,例如针对值 “wangwu” 和三个不同的哈希函数分别生成了哈希值 1、4、7,则上图转变为:
在这里插入图片描述
  Ok,我们现在再存一个值 “zhangshan”,如果哈希函数返回 3、4、8 的话,图继续变为:在这里插入图片描述
  值得注意的是,4 这个 bit 位由于两个值的哈希函数都返回了这个 bit 位,因此它被覆盖了。现在我们如果想查询 “lishi” 这个值是否存在,哈希函数返回了 1、5、8三个值,结果我们发现 5 这个 bit 位上的值为 0,说明没有任何一个值映射到这个 bit 位上,因此我们可以很确定地说 “lishi” 这个值不存在。而当我们需要查询 “wangwu” 这个值是否存在的话,那么哈希函数必然会返回 1、4、7,然后我们检查发现这三个 bit 位上的值均为 1,那么我们可以说 “wangwu” 存在了么?答案是不可以,只能是 “wangwu” 这个值可能存在。
  
这是为什么呢?答案跟简单,因为随着增加的值越来越多,被置为1 的 bit 位也会越来越多,这样某个值 “ergou” 即使没有被存储过,但是万一哈希函数返回的三个 bit 位都被其他值置位了 1 ,那么程序还是会判断 “ergou” 这个值存在。

很显然,过小的布隆过滤器很快所有的 bit 位均为 1,那么查询任何值都会返回“可能存在”,起不到过滤的目的了。布隆过滤器的长度会直接影响误报率,布隆过滤器越长其误报率越小。

另外,哈希函数的个数也需要权衡,个数越多则布隆过滤器 bit 位置位 1 的速度越快,且布隆过滤器的效率越低;但是如果太少的话,那我们的误报率会变高。

2、位图索引

先举例:
在这里插入图片描述
如果用户查询的列的基数非常的小, 即只有的几个固定值,如性别、婚姻状况、行政区等等。要为这些基数值比较小的列建索引,就需要建立位图索引
对于性别这个列,位图索引形成两个向量,男向量为10100…,向量的每一位表示该行是否是男,如果是则位1,否为0,同理,女向量位01011。
在这里插入图片描述
对于婚姻状况这一列,位图索引生成三个向量,已婚为11000…,未婚为00100…,离婚为00010…。
在这里插入图片描述
当我们使用查询语句“select * from table where Gender=‘男’ and Marital=“未婚”;”的时候 首先取出男向量10100…,然后取出未婚向量00100…,将两个向量做and操作,这时生成新向量00100…,可以发现第三位为1,表示该表的第三行数据就是我们需要查询的结果。
在这里插入图片描述
bloom于位图索引的区别

Bloom:通过一个bit向量,判断某值是否存在;
位图索引:从多个bit向量中选出一个,代表的是结果集。

3、稀疏索引(Cassandra 、kafka都有使用)

稀疏索引只为数据文件的每个存储块设一个键-指针对,它比稠密索引节省了更多的存储空间,但查找给定值的记录需更多的时间。只有当数据文件是按照某个查找键排序时,在该查找键上建立的稀疏索引才能被使用,而稠密索引则可以应用在任何的查找键。

如图所示,稀疏索引只为每个存储块设一个键-指针对。键值是每个数据块中第一个记录的对应值。
在这里插入图片描述
顺序文件上的稀疏索引

我们假定数据文件已排序,且其键值为连续的10的倍数,直至某个较大的数。我们还继续假定每个存储块可存放四个键-指针对。这样,第一个索引存储块中为前四个数据存储块的第一个键值的索引项,它们分别是10、30、50和70。按照前面假定的键值模式,第二个索引存储块中为第五至第八个数据存储块的第一个键值的索引项,它们分别是90、110、130和150。图中我们还列出第三个索引存储块存放的键值,它们分别是假设的第九至第十二个数据存储块的第一个键值。

在已有稀疏索引的情况下,要找出查找键值为K的记录,我们得在索引中查找到键值小于或等于K的最大键值。由于索引文件已按键排序,我们可以使用二分查找法来定位这个索引项,然后根据它的指针找到相应的数据块。现在我们必须搜索这个数据块以找到键值为K的记录。

4、密集索引

我们在记录上建立稠密索引,它是这样一系列存储块:块中只存放记录的键以及指向记录本身的指针,指针就是一个指向记录或存储块地址。既然我们假定查找键和指针所占存储空间远小于记录本身,我们就可以认为存储索引文件比存储数据文件所需存储块要少得多。当内存容纳不下数据文件,但能容纳下索引文件时,索引的优势尤为明显。这时,通过使用索引文件,我们每次查询只用一次I/O操作就能找到给定键值的记录。

下图所示为一个建立在顺序文件上的稠密索引。在这里插入图片描述
顺序文件(右)上的稠密索引(左)

第一个索引块存放指向前四个记录的指针,第二个索引块存放指向接下来的四个记录的指针,依此类推。

稠密索引支持按给定键值查找相应记录的查询。给定一个键值K,我们先在索引块中查找K。当找到K后,我们按照K所对应的指针到数据文件中找到相应的记录。似乎在找到K之前我们需要检索索引文件的每个存储块,或平均一半的存储块。然而,由于有下面几个因素,基于索引的查找比它看起来更为有效:

a.索引块数量通常比数据块数量少。
b.由于键被排序,我们可以使用二分查找法来查找K。若有n个索引块,我们只需查找log2n个块。
c.索引文件可能足够小,以至可以永久地存放在主存缓冲区中。要是这样的话,查找键K时就只涉及主存访问而不需执行I/O操作。

5、B-tree

B-Tree是一种平衡查找树。主要用于DB从磁盘中快速查找数据。

DB从磁盘读取数据到内存时是以磁盘块(block)或磁盘块的N倍为基本单位的,位于同一个磁盘块中的数据会被一次性读取出来,而不是需要什么取什么。

例如:InnoDB存储引擎中以页(page)为其磁盘管理的最小单位,默认每个页的大小为16KB:因此InnoDB每次申请磁盘空间时都会是若干地址连续磁盘块来达到页的大小16KB。

下图是一个3阶B-Tree,每个节点占用一个盘块的磁盘空间,节点上有两个升序排序的关键字和三个指向子树根节点的指针,指针存储的是子节点所在磁盘块的地址。两个关键词划分成的三个范围域对应三个指针指向的子树的数据的范围域。
一条记录为一个二元组[key, data] ,key为记录的键值,对应表中的主键值,data为一行记录中除主键外的数据。对于不同的记录,key值互不相同。
在这里插入图片描述
一棵m阶的B-Tree有如下特性:

  1. 每个节点最多有m个孩子。
  2. 除了根节点和叶子节点外,其它每个节点至少有Ceil(m/2)个孩子。
  3. 若根节点不是叶子节点,则至少有2个孩子
  4. 所有叶子节点都在同一层,且不包含其它关键字信息
  5. 每个非叶节点包含n个关键字信息(P0,P1,…Pn, k1,…kn)
  6. 关键字的个数n满足:ceil(m/2)-1 <= n <= m-1
  7. ki(i=1,…n)为关键字,且关键字升序排序。
  8. Pi(i=1,…n)为指向子树根节点的指针。P(i-1)指向的子树的所有节点关键字均小于ki,但都大于k(i-1)

6、B+tree

B+Tree是在B-Tree基础上的一种优化,从B-Tree结构图中可以看到每个节点中不仅包含数据的key值,还有data值。而每一个数据块的存储空间是有限的,如果data数据较大时将会导致每个节点能存储的key的数量很小,当存储的数据量很大时同样会导致B-Tree的深度较大,增大查询时的磁盘I/O次数,进而影响查询效率。
B+Tree中,所有数据记录节点都是存放在叶子节点上,而非叶子节点上只存储key值信息,这样大大加大每个节点存储的key值数量,降低B+Tree的高度。
在这里插入图片描述

B+Tree相对于B-Tree有几点不同:

	a.非叶子节点只存储键值信息。
	b.所有叶子节点之间都有一个链指针。
	c.数据记录都存放在叶子节点中。

7、LSM:

在这里插入图片描述
LSM树(Log-Structured Merge Tree)

可以看作一个N阶合并树,数据写操作(包括插入、修改、删除)都是内存中进行,并且都会创建一个新记录(修改会记录新的数据值,而删除会记录一个删除标志),而这些数据在内存中仍然是一颗排序树,当数据量超过内存阈值后,会将这个排序树和磁盘最新的排序树合并。当这颗排序树数据量超过内存阈值后,和磁盘上下一级的排序树合并,合并过程中,会用最新更新的数据覆盖旧的数据(或者记录成不同版本)
理论上,可以是内存中树的一部分和磁盘中第一层树做合并,对于磁盘中的树直接做update操作有可能会破坏物理block的连续性,但是实际应用中,一般LSM树有多层,当磁盘中的小树合并成一个大树的时候,可以重新排好顺序,使得block连续,优化读性能。

LSM树的特点:将对数据的修改增量保持在内存中,达到指定的大小限制后将这些修改操作批量写入磁盘

LSM树的核心思想: 放弃部分读性能,提高写性能

代表数据库:cassandra、HBase等非关系型数据库

8、倒排索引与正排索引

对于搜索引擎来讲。

正排索引是文档 Id 到文档内容、单词的关联关系,也就是说可以通过 Id获取到文档的内容。

Doc      Terms
-----------------------------------------------------------------
Doc_1 | brown, dog, fox, jumped, lazy, over, quick, the
Doc_2 | brown, dogs, foxes, in, lazy, leap, over, quick, summer
Doc_3 | dog, dogs, fox, jumped, over, quick, the

倒排索引是单词到文档 Id 的关联关系,也就是说了一通过单词搜索到文档 Id。

Term      Doc_1   Doc_2   Doc_3
------------------------------------
brown   |   X   |   X   |
dog     |   X   |       |   X
dogs    |       |   X   |   X
fox     |   X   |       |   X
foxes   |       |   X   |
in      |       |   X   |
jumped  |   X   |       |   X
------------------------------------

搜索的时候,要依靠倒排索引;排序的时候,需要依靠正排索引,看到每个document的每个field,然后进行排序,所谓的正排索引,其实就是doc values。
elasticsearch doc values

倒排索引数据结构

倒排索引的核心分为两部分,

第一、单词词典(Term Dictionary),记录所有文档的单词以及单词到倒排列表的关联关系。采用 B+ 树和哈希拉链法去存储单词的词典,以满足高性能的插入与查询。

第二、倒排列表(Posting List),它记录了单词对应文档的结合,倒排列表是由倒排索引项(Posting) 组成,倒排索引项包含:
	1.文档 ID:用于获取原始信息
	2.词频(TF,Term Frequency):该单词在文档中出现的次数,用于相关性评分
	3.位置(Position):单词在文档中分词的位置,用于语句搜索(Phrase Query)
	4.偏移(Offset):记录单词的开始结束位置,实现高亮显示(比如用 GitHub 搜索的时候,搜索的关键词会高亮显示)

倒排索引结构图:
在这里插入图片描述

一个倒排索引是由单词词典(Term Dictionary)和倒排列表(Posting List)组成的,单词词典会记录倒排列表中每个单词的偏移位置。比如当搜索 Allen 的时候,首先会通过单词词典快速定位到 Allen,然后从 Allen 这里拿到在倒排列表中的偏移,快速定位到在倒排列表中的位置,从而真正拿到倒排索引项 [12,15](这里只是列了下 Document ID,其实是像上面讲的包含 4 项信息的项),拿到这个项可以去索引上拿到原始信息,可以去计算打分排序返回给用户。

ElasticSearch 倒排索引

在 ElasticSearch 中的文档是基于 Json 格式的,其中一个文档包含多个字段,每个字段都会有自己的倒排索引。在 Mapping 中可以去设置对某些字段不做索引,这样做可以节省存储空间,但同时也会导致这个字段无法搜索了。

比如一个文档,其中包含两个字段 username 和 job:

{
 "username":"wupx",
"job":"programmer"
}

在构建索引的时候是根据字段构建的,那么 ES 中 username 会有一个倒排索引,job 也会有一个倒排索引。

在这里插入图片描述

9、Oracle反转键索引(REVERSE)

定义:

反向索引作为B-tree索引的一个分支,主要是在创建索引时,针对索引列的索引键值进行字节反转,进而实现分散存放到不同叶子节点块的目的。

解决的问题:

使用传统的B-tree索引,当索引的列是按顺序产生时,相应的索引键值会基本分布在同一个叶块中。当用户对该列进行操作时,难免会发生索引块的争用。使用反向索引,将索引列的键值进行反转,实现顺序的键值分散到不同的叶块中,从而减少索引块的争用。例如:键值1001、1002、1003,反转后1001、2001、3001,进而分散到不用的叶子节点块中。

应用场景:

索引块成为热点块

优点:

降低索引叶子块的争用问题,提升系统性能。

缺点:

对于范围检索,例如:between,>,<时,反向索引无法引用,进而导致全表扫面的产生,降低系统性能。

实例

create index idx_REV_TEST on TEST(EMPNO) REVERSE;
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值