建议先倒杯水,这次的货是真的干

问题

在上节文章中,我们留下了一个问题,在之前的es版本,文档的还有一个元数据_type?你知道是做什么的吗?为什么后期的版本进行了取消?

首先,我们先看看第一个问题,type到底是做什么的。其实在Elasticsearch 6.x版本就开始只支持一种type作为过渡,8.0版本就正式移除了type。在上节我们聊到。文档的集合是索引,索引是一系列有着相同数据结构的文档。其实在6.0之前,文档并不是由索引确定的,而是由索引和类型(Type)决定的。

因此在6.0之前,如果索引可以类比关系型数据里的库,而type可以类比成关系型数据库里面的表。也就是同一个索引里面,可以有不同的类型。举个例子来说:

索引es-learn-6-info有一个Typepeason

{
  "id":1,
  "name":"li si",
  "class":6
}

还有一个Typeclass

{
  "id":6,
  "leader":"ba yi",
  "loc":"6f"
}

可以看到,不同的Type有着不同的数据结构,那么这种情况下我们如何查询呢?

GET es-learn-6-info/class,peason/_search
{
  "query": {
    "match": {
      "id": 6
    }
  }
}

其实到这里,我们发现了几个问题:

第一,相同的索引下,有着不同的类型,类型中有可能有着相同定义的字段,这时候在底层是否会造成混乱?Lucene是否支持?

第二,一个索引下,不同的Type有着不同的字段,这样对于文档的压缩是一个难以攻克的问题。

个人认为,这样的两个原因,让Elasticsearch放弃了Type,而将索引变为了结构相同的文档的集合。

索引

正排索引

索引相当于图书的目录,可以在目录中找到对应的页码,快速定位到对应的数据,这时候有两种思考的方向。一般我们会给文章一个标识,再对文章进行分析,比如文章有哪些词语啊,这些词语出现了多少次,这篇文章在什么文件中的位置,这样更方便我们去查找。

正排索引.png

那我们怎么去查找一篇包含某个词语的文章呢?就像这样的结构,当我们需要查找某个信息的时候,我们可以去文档的列表里面遍历查找。首先需要找到文档(1),之后对文档下存储的词语进行遍历匹配,提取出需要的信息,再到下一个文档,重复查找操作。

倒排索引

在这里,还是引用之前的一个故事,这个故事以前讲过,为了系列的完整性,我还是在这里进行重述,看过的朋友可以再熟悉一次。

假设八佾有个朋友,但这个朋友,不是八佾本人。在农历新年给领导写信,但是有两个领导是八佾的朋友很想骂sillybee的,于是他大胆的在信中这么写。

doc1:"dear leader ,you sillybee"

doc2:"dear leader ,i have to thank a happy silybe new year "

doc3:"dear leader ,i have to say happy new year"

三封已经封好的信, 八佾的朋友是个聪明的IT从业工作者(但他远没你想的那么聪明),担心自己分不清三封信,于是 他记录下了这么一张表。

倒排索引表.png

我想帮他向你们解释一下这张表,第一列是出现的单词,第二列是单词出现的文档位置。例如,dear这个单词出现在文档1,文档2和文档3。sillybee这个单词出现在文档1中,这样就能快速找到文档出现的位置了。

倒排索引原理图.png

所以倒排索引的数据结构相比于正排索引就变成了这个样子。从词语去查找文档出现的位置,这样是不是更加快捷,就像八佾的朋友一样,立即就能找到关键词的文档在什么位置。

八佾的朋友,在贤者时间的时候冷静下来,毕竟还要为五斗米折腰,骂人的信不能寄出去,想要剔除出来,于是自信的通过上面的表,找到了sillybee这个词项,找到了对应的记录,第一 封信中有,于是快速的找到了骂人的信,后来他兴高采烈的把信寄了出去。但是粗心的他把sillybe,写成了silybee,但是领导可不会get不到这个单词是骂人的意思。看来朋友并没有用好这 个倒排索引表。

Elasticsearch想到了这里,提供了丰富的文档处理。比Analyzer分词器,会对文本中的词按一定规则进行切分。因为不同的语言分词的规则不一样,比如中文和英文就有着不同的切分规则,还有一些模糊匹配,停用词等的,这个后面有机会我们再详细的探究。

索引的压缩算法

倒排索引的id.png

这是一个倒排索引的结构,DOC ID存储的文档列表,可以看到这个列表的存储代价是很大的,所以必须要对这部分进行压缩。

FOR

FOR的全称是Frame Of Reference,主要思想是先对DOC ID列表进行排序,之后使用差值的方式进一步压缩,这样需要表示的数字更小,占的位数少,需要空间就更少。这么说可能太抽象了。

压缩的id实例.png

第①步:先对doc id的列表进行升序排列,众所周知一个整数在计算机中用4个字节表示,因此6个数占得字节是24个。

第②步:对doc id进行向前求差得出delta list,将大数化小

第③步:对delta list进行分块,使得更小的数占更小的字节,用一个字节来表示,该块中的每个数占多大的位数,如图左边的绿色表示每个数都是7位,也就是7位一个数。右侧绿色表示的是每个数是5位,这样在反编码的时候,我们可以再恢复到原先的doc id。

RBM

FOR的思想是拉链归并,,Elasticsearch对一些常用的过滤器进行了缓存,用来加速一些过滤器的执行,因此带来了几个问题:

  1. 因为不能缓存所有的过滤器,所以压缩比例对于倒排索引的编码匹配来说不那么重要
  2. 我们需要加速一些重复执行的过滤器,因此一个好的数据结构来说很重要
  3. 过滤器存储在内存中,而正排表基本上是在磁盘上

面对此种情况,Elasticsearch列举了一些对应的方案,进行了均衡。

第一种方式:整型数组

最直接的选择就是数组,因为数据的遍历性,可以极大的提速。然而压缩性却很难保证。如果你需要存储100Mb的文档,那么你就至少需要400Mb的空间。

第二种方式:bitmap

位图是一个数组,其中每个条目只占用一位,因此它们只有两个可能的值:0 或 1。

为了知道 docID 是否包含在位图中,需要读取索引 docID 处的值。 0 表示该集合不包含此 docID,而 1 表示该集合包含此 docID。迭代需要计算连续的零,这实际上非常快,因为 CPU 有专门的指令。如果我们与整型 相比,密集过滤器的内存使用率要好得多,因为我们现在只需要 100M 位 = 12.5MB。

但是这种情况下会有稀疏集的问题:虽然我们的第一个选项每次匹配需要 4 个字节,但现在无论有多少匹配,我们都需要 12.5MB 的内存。

第三种方式: roaring bitmaps

RBM是为了兼顾数组的遍历性和位图的低存储空间。具体的过程是这样的:

首先根据docId的高16位进行分块,每个块最多包含65535个,例如第一个块存储的是docId(0-65535),第二个块表示的docId(65536-131071)。这样用来解决bitmap的稀疏集的问题,使得每个块里的docId更加的稠密。这样我们就可以使用 bitmap的特性,但是此时数组的特性如何使用呢?

第二步,对docid进行分配,使得数据稠密,范围在集中的块里

第三步,上面部分解决了数据稀疏性的问题,但是如何用上数组的易于遍历呢?此时Elasticsearch做了一个实验。

4096实验.png

根据图中可以看到当4096之后的数组内存使用是高于bitmap的,所以如果docId的个数小于4096个的时候就使用数组,如果高于4096个的时候就使用bitma,这样做出了均衡。

问题

这一章,我们学习了type的问题,也学习了倒排索引的原理和压缩,这一节必须说干货满满。同时,我这边有个问题想跟大家讨论,es中除了压缩doc id,你觉得还有哪些地方可以压缩呢?

如果你有任何问题,欢迎与我讨论。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值