那么到这里我们就可以思考这个问题了,假如索引值本身就很大,那么 B+ 树是不是性能会急剧下降呢?答案是肯定的,因为当索引值很大的话,一个节点能存储的数据会大大减少(一个节点默认是 16kb 大小),B+ 树就会变得更深,每次查询数据所需要的 IO 次数也会更多。而且全文索引就是需要支持对大文本进行索引的,从空间上来说 B+ 树不适合作为全文索引,同时 B+ 树因为每次搜索都是从根节点开始往下搜索,所以会遵循最左匹配原则,而我们使用全文搜索时,往往不会遵循最左匹配原则,所以可能会导致索引失效。

总结起来 B+ 树不适合作为全文搜索索引主要有以下两个原因:

  • 全文索引的文本字段通常会比较长,索引值本身会占用较大空间,从而会加大 B+ 树的深度,影响查询效率。

  • 全文索引往往需要全文搜索,不遵循最左匹配原则,使用 B+ 树可能导致索引失效。

[](()全文检索

=================================================================

在全文检索当中,我们需要对文档进行切词处理,切好之后再将切出来的词和文档进行关联,并进行索引,那么这时候我们应该如何存储关键字和文档的对应关系呢?

[](()正排索引


可能大家都知道,在全文检索中(比如:Elasticsearch)用的是倒排索引,那么既然有倒排索引,自然就有正排索引。

正排索引又称之为前向索引(forward index)。我们以一篇文档为例,那么正排索引可以理解成他是用文档 id 作为索引关键字,同时记录了这篇文档中有哪些词(经过分词器处理),每个词出现的次数已经每个词在文档中的位置。

但是我们平常在搜索的时候,都是输入一个词然后要得到文档,所以很显然,正排索引并不适合于做这种查询,所以一般我们的全文检索用的都是倒排索引,但是倒排索引却并不适合用于聚合运算,所以其实在 es 中的聚合运算用的是正排索引。

[](()倒排索引


倒排索引又称之为反向索引(inverted index)。和正排索引相反,倒排索引使用的是词来作为索引关键字,并同时记录了哪些文档中有这个词。

在这里我们以一个英文文档为例子,之所以选择用英文文档是因为英文分词比较简单,直接以空格进行分词即可,而中文分词相对比较复杂。

我们以 Elasticsearch 官网中下面两句话作为两位文档来分析:

Elasticsearch is the distributed search and analytics engine at the heart of the Elastic Stack.

Elasticsearch provides near real-time search and analytics for all types of data.

根据上面两句话,假设我们可以得到下面这样的一个索引结构:

| term index | term dictionary | Posting list TF |

| — | — | — |

| term 索引 | elasticsearch | [1,2] |

| term 索引 | search | [1,2] |

| term 索引 | elastic | [1] |

| term 索引 | provides | [2] |

其中:

  • term index:顾名思议,这个是为 term(经过分词后的每个词) 建立的索引,也就是通过这个索引可以快速找到当前 term 的位置,从而找到对应的 Posting list。因为在 es 中,会为每个字段都建立索引(默认存储在内存中),所以当我们的数据量非常大的时候,就需要能快速定位到这个词对应的索引所在的内存位置,所以就单独为每个 term 建立了索引,这个索引一般可以选择哈希表或者 B+ 树进行索引存储。

  • term dictionary:记录了文档中去重后的所有词(经过分词器处理)。

  • Posting list TF:记录了含有当前词的文档以及当前词出现在文档的位置(偏移量),该项信息是一个数组,上面表格中为了简单只列举了文档 id,实际上这里会存储很多信息。

这时候假如我们搜索 Elasticsearch Elastic 这样的关键字,那么会经过以下步骤:

  1. 对输入的关键字进行分词处理,得到两个词:elasticsearchelastic(经过分词器之后大写字母都会转化成小写字母)。

  1. 然后分别用这两个词进行搜索,搜索之后,发现 elasti **《一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》无偿开源 威信搜索公众号【编程进阶路】** csearch 在两个文档中都有出现,而 elastic 只在文档一中出现。

  2. 最终的搜索结果就是文档一和文档二都返回,但是因为文档一两个词都命中了,所以相关度(分数)更高,于是文档一会排在文档二前面,这就是算分的过程。不过需要注意的是,实际的这种相关度分数算法不会这么简单,而是有专门的算法来计算,命中词多的并不一定会出现在前面。

[](()倒排索引如何存储数据

知道了倒排索引的搜索过程,那么倒排索引的数据又是如何存储的呢?

回答这个问题之前我们先来看另一个问题,那就是建立索引的目的是什么?最直接的目的肯定是为了加快检索速度,而为了达到这个目的,那么在不考虑其他因素的情况下,必然是需要占用的空间越少越好,而为了减少占用空间,可能就需要压缩之后再进行存储,而压缩之后又涉及到解压缩,所以采用的压缩算法也需要能达到快速压缩和解压的目的。

[](()FOR 压缩

FOR 压缩算法即 Frame Of Reference。这种算法比较简单,也有一定的局限性,因为其对存储的文档 id 有一定要求。

假设现在有一亿个文档,对应的文档 id 就是从 1 开始自增。假设现在关键字 elasticsearch 存在于 1000W 个文档中,而这 1000W 个文档恰好就是从 11000W,那么假如不采用任何压缩算法,直接进行存储需要占用多少空间?

int 类型占用了 4 个字节,而 1000W 这个数量级需要 224 次方,也就是说如果用二进制来存储,在不考虑符号位的情况下也需要 24bit 才能存储,而因为 Posting list TF 是一个数组,所以为了能解析出数据,文档 id=1 的数据也需要用 24bit 来进行存储,这样就会极大的浪费了空间。

为了解决这个问题,我们就需要使用 FOR 算法,FOR 算法并不直接存储文档 id,而是存储差值,像这种这么规律的文档 id,差值都是 1,而 1 转成二进制就可以只使用 1bit 进行存储,这样就只需要 1000Wbit 的空间来进行存储就够了,相比较直接存储原始文档 id 的情况下,这种场景采用 FOR 算法大大减少了空间。

上面举的这个例子是比较理想的情况,然而实际上这种概率是比较小的,那我们再来看下面这一组文档 id

1,9,15,45,68,323,457

这个数组计算差值后得到下面这个数组:

8,6,30,23,255,134

这个时候如果还是直接用普通差值的算法,虽然也能节省空间,但是却并不是最优的一种解决方案,那么这个时候有没有一种更高效的方法来进行存储呢?

我们观察下这个差值数组,发现这个数组可以进一步拆分成两组:

  • [8,6,30,23]:这一组最大值为 30,只需要 5 个比特就能进行存储。

  • [255,134]:这一组最大值为 255,需要 8 个比特就能存储。

这么拆分之后,原始数据需要用 32*7=224 个比特(原始数据直接用 int 存储),普通差值需要 8*6=48 个比特,而经过分组差值拆分之后只需要 5*4+8*2=36 个比特,进一步压缩了空间,这种优势随着数据量的增加会更加明显。

但是不管采用哪种方案都有一个问题,那就是进行差值或者拆分之后,怎么还原数据,解压的时候怎么知道差值数组内的元素占用空间大小?

所以对每一个数据,还需要一块一个字节的空间大小来存储当前数组内元素占用的比特数,所以分组并不是越细越好,假如对每一个差值元素都单独存储,那么反而会比不分组更浪费空间,反之,如果每个分组内的元素足够多,那么存储占用空间的这一个字节反带来的影响就会更小或者忽略不计。

[](()RBM 压缩

上面例子中介绍的差值都不会大相径庭,那么假如我们差值计算之后得到的数组,其每个元素差别都很大呢?比如说下面这个文档 id 数组:

1000,62101,131385,132052,191173,196658

这个数组大家可以去计算一下差值,计算之后会发现一个大一个小,两个差值之间差距很大,所以这种方式就不适合于用 FOR 压缩,所以我们就需要有另外的压缩算法来提升效率,这就是 RBM 压缩。

RBM 压缩算法即 Roaring Bitmap,是在 2016 年由 S. Chambi、D. Lemire、O. Kaser 等人在论文[《Better bitmap performance with Roaring bitmaps》](()与[《Consistently faster and smaller compressed bitmaps with Roaring》](()中提出来的。

RBM 压缩算法的核心思想是:将 32 位无符号整数按照高 16 位进行划分容器,即最多可能有 65536container。因为 65536 实际上就是 216 次方,而一个无符号 int 类型正好是需要 32 位进行存储,划分为高低位正好两边都是 16 位,也就是最多 65536 个。

划分之后根据高 16 位去找 container(比如高 16 位计算的结果是 1 就去找 container_12 就去找 container_2,依次类推),找到之后如果发现容器不存在,那么就会新建一个容器,并且把低 16 位存入容器内,如果容器存在,就直接将低 16 位存入容器。

这样就会出现一个现象:那就是容器最多有 65536 个,而每个容器内的元素也恰好最多是 65536 个元素

也就是上面的数组经过计算就会得到以下容器(container_1 没有元素):

如果说大家觉得上面的高低 16 位不好理解,那么可以这么理解,我们把数组中的元素全部除以 65536,对其取模,每得到一个模就创建一个容器,而其余数就放入对应的模所对应的容器中。因为一个 int 类型就是 232 次方,正好是 65536 的平方。

经过运算之后得到容器,那么容器中的元素又该如何进行存储呢?可以选择直接存储,也可以选择其他更高效的存储方式。在 RBM 算法中,总共有三种容器类型,分别采用不同的方法来存储容器中的元素:

  • ArrayContainer

ArrayContainer 采用 short 数组来进行存储,因为每个容器中的元素最大值就是 65535,采用 2 个字节进行存储。这种存储方式的特点是随着元素个数的增多,所需空间会一直增大。

  • BitmapContainer

BitmapContainer 采用位图的方式进行存储,也就是固定创建一个 65536 长度的容器,容器中每个元素只用一个比特进行存储,某一个位置有元素则存储 1,没有元素则存储为 0。这种存储方式的特点是空间固定就是占用 65536 个比特,也就是大小固定为 8kb

  • RunContainer

RunContainer 比较特殊,在特定场景下会使用,比如文档 id1-100 是连续的,那么采用这种容器就可以直接存 1,99,表示 1 后面有 99 个连续的数字,再比如 1,2,3,4,5,6,10,11,12,13 可以被压缩为 1,5,10,3,表示 1 后面有 5 个连续数字,10 后面有 3 个连续数字。

至于每次存储采用什么容器,需要进行一下判定,比如 ArrayContainer,当存储的元素少于 4096 个时,他会比 BitmapContainer 占用更少空间,而当大于 4096 个元素时,采用 ArrayContainer 所需要的空间就会大于 8kb,那么采用 BitmapContainer 就会占用更少空间。

[](()倒排索引如何存储


前面我们讲了 es 中的倒排索引采用的是什么压缩算法进行压缩,那么压缩之后的数据是如何落地到磁盘的呢?采用的是什么数据结构呢?

[](()字典树(Tria Tree)

字典树又称之为前缀树(Prefix Tree),是一种哈希树的变种,可以用于搜索时的自动补全、拼写检查、最长前缀匹配等。

Elasticsearch 中为什么选择倒排索引而不选择 B 树索引相关推荐

  1. Elasticsearch中的倒排索引和读写操作原理解析

    目录 前言 一.倒排索引 二.Elasticsearch中的基本概念 三.写操作原理 四.读操作原理 前言 在实际的生产环境中,起初启用Elasticsearch(下称ES)是为了解决模糊查询的问题. ...

  2. ElasticSearch中的集群、节点、索引、文档、类型是什么?

    群集:一个或多个节点(服务器)的集合,它们共同保存您的整个数据,并提供跨所有节点的联合索引和搜索功能.群集由唯一名称标识,默认情况下为"elasticsearch".此名称很重要, ...

  3. 储存引擎InnoDB 索引选择 为何是B+树 而不是 B树 哈希表

    一:概述 首先需要澄清的一点是,MySQL 跟 B+ 树没有直接的关系,真正与 B+ 树有关系的是 MySQL 的默认存储引擎 InnoDB,MySQL 中存储引擎的主要作用是负责数据的存储和提取,除 ...

  4. 为什么 MySQL 使用 B+ 树,而不是 B 树或者 Hash?

    为什么 MySQL 使用 B+ 树是面试中经常会出现的问题,很多人对于这个问题可能都有一些自己的理解,但是多数的回答都不够完整和准确. 大多数人都只会简单说一下 B+ 树和 B 树的区别,但是都没有真 ...

  5. B+树 范围查询_为什么 MySQL 使用 B+ 树,而不是 B 树或者 Hash?

    Java技术栈 www.javastack.cn 关注优质文章 为什么 MySQL 使用 B+ 树是面试中经常会出现的问题,很多人对于这个问题可能都有一些自己的理解,但是多数的回答都不够完整和准确.大 ...

  6. mysql索引为啥要选择B+树 (下)

    有读者在 mysql索引为啥要选择B+树 (上) 上篇文章中留言总结了选择 B+ 树的原因,大体上说对了,今天我们再一起来看看具体的原因. 索引为什么要保存在硬盘中 首先要明白几个概念,服务器存储一般 ...

  7. MySQL中B+树索引,聚簇索引,二级索引,辅助索引,回表,索引生效条件

    对于MySQL,我们经常说调优有一个手段就是加索引,那么为什么加索引能够优化查询,是不是加了索引查询就快了 ? 在MySQL中,存储的单元并不是按照我们理解的一条一条记录,而是按照页来进行存储的,My ...

  8. 【Logstash】Logstash:把MySQL数据导入到Elasticsearch中

    1.概述 转载:https://www.cnblogs.com/sanduzxcvbnm/p/12076487.html Logstash:把MySQL数据导入到Elasticsearch中 前提条件 ...

  9. Elasticsearch中字段类型(Field Type)详解

    ElasticSearch 7.7 字段类型(Field datatype)详解 字符串, object, 数值, 日期, 数组, 0x00 字符串: text, keyword 5.0以后,stri ...

最新文章

  1. 【OpenCV 4开发详解】图像腐蚀
  2. 潘建伟团队最新研究成果登上Nature:首次实现1120公里长距离无中继纠缠量子密钥分发...
  3. DPDK — DPDK APP 的指令行参数
  4. python的celery的面试_python 面试
  5. How is parsed BeanDefinition registered
  6. [设计模式]合成复用原则
  7. 分组,采样和批处理– Java 8中的自定义收集器
  8. Javascript之创建对象(原型模式)
  9. 10个一行代码就能搞定的编程技巧
  10. char[] 和char*之间的相互转换
  11. 漫谈 Gentoo 中文社区的建设
  12. Flink学习笔记02:Flink三种运行模式
  13. flume消费kafka数据太慢_kafka补充01
  14. 危机中转型,如何获得领导的支持?
  15. 尼姑一般怎么自称_电池又一重大事故!选铅酸,还是锂电?听听电池老师傅怎么说!...
  16. y7000 安装linux双系统,联想拯救者Y7000安装双系统:win10 + ubuntu16.04 (GTX1060显卡)...
  17. win10登录时显示PIN码不可用,转到设置重新设置PIN码
  18. [芭比公主系列][国英双语]芭比之十二芭蕾舞公主 Barbie in The 12 Dancing Princesses
  19. 软件设计师证书重要吗?
  20. 当年也是翩翩少年,如今落得秃顶大叔,程序员秃顶算工伤吗?

热门文章

  1. 函数u=(x,y,z)在点P处延方向向量n的方向导数的计算
  2. 关于网线水晶头的接法详解(2)
  3. 河北工业大学城市学院2019级 Java企业级系统管理期末复习资料
  4. 计算机固态硬盘装系统,SSD装系统不求人 9分钟U盘快速装win7
  5. icloud 如何同步_如何使用iCloud同步联系人,提醒等
  6. 金蝶服务端调用OSF服务
  7. R项目:使用R分析人力资源数据
  8. python cv2图像质量压缩
  9. python math模块
  10. DREA:基于多样性排序的多目标优化进化算法