【ElasticSearch 大白话笔记】01. 倒排索引和正排索引的相关知识

1 倒排索引

1.1 背景

当前的搜索引擎,为什么能通过某些关键字,就能快速搜出相关的文章出来?
答案就是:把不同网站上的文章先收录下来,然后对文章内容进行分段、分词 (term),最后构造一个这样的索引:

**[分词1] to [文档1, 文档2, 文档3]**
**[分词2] to [文档1, 文档34, 文档35]**
**[分词3] to [文档23]**

因为索引的key是一个分词数组,只要先遍历这些 key,就能快速找到对应的文档 id / 文档链接

1.2 定义

背景中的索引,其实就是倒排索引,它与传统索引的区别是,传统索引的 keyidvalue 是对应的某个或者某些字段值 (详情可以参考下基于 B-TreeMySQL 下的索引) ,而倒排索引正好相反,为某个字段建倒排索引后 (mapping.properties.字段.index = true) , 索引的 key 就是对应字段的 value 中的分词, 然后指向一个 doc_id 的链表

1.3 优势

这样的索引有什么好处?
根据关键字找文档就找得快,只需要遍历这个倒排索引就能找出对应的文章

1.4 生成/更新时机

跟普通的索引一样,在文档被插入的时候,倒排索引就会被生成和更新

1.5 数据结构

找了半天,它的数据结构大概是这样的
(原图在某乎上面贴来的,它的源在哪我也不知道)
在这里插入图片描述
三个部分,分别是字典树构造的 term index,保存在内存中,所以可以快搜,term index 找到对应的 term,最后根据 term dictionary 找到对应的 posting list

1.5.1 分词索引(Term Index)

FST 结构,大概是长这样
在这里插入图片描述
特点就是省内存,FST压缩率一般在3倍~20倍之间,相对于TreeMap/HashMap的膨胀3倍,内存节省就有9倍到60倍

1.5.2 分词字典(Term Dictionary)

Term dictionary 在磁盘上是以分 block 的方式保存的,一个 block 内部利用公共前缀压缩,比如都是 Ab 开头的单词就可以把 Ab 省去。这样 Term dictionary 可以比 b-tree 更节约磁盘空间。

1.5.3 Posting List

这个东西在做联合索引查询的时候有学问,首先做联合索引涉及到两种数据结构,分别是 bitsetskip list

1 bitset (当查询命中内存中的 filter 时)

首先看看 bitset 转换

### bitset to posting list 
[1,0,1,1,0,0,1,0,0,1] -> [1, 3, 4, 7, 10]

利用 bitset 进行条件筛选的时候十分简单,找到 Posting List,然后转换 bitset,接着进行 AND 操作就好了
Lucene 会用 Roaring Bitmapbitset 进行压缩,压缩原理也很简单,就是记录每个 bit 前面有多少个 0 就行,具体的原理看下下图
在这里插入图片描述

2 skip list (查询没有命中,直接在磁盘中进行数据过滤)

跳表的话大家都知道,就是这样的:
在这里插入图片描述
由于 posting list 很长,所以 Lucene 会先对它进行分块,接着再压缩,分块后的压缩方式称为 *FOR (Frame of Reference) * ,压缩方式的算法见下图
在这里插入图片描述
压缩方式比较简单

  1. 将 [73, 300, 302, 332, 343, 372] 转为 [73, 227, 2, 30, 11, 29],就是记录每个 id 的增量,因为 posting list 是有序的,所以这个方案才可行
  2. posting list 分块,每一块不超过256,这样子的话,增量也不会超过256了,不超过256的话,每一个增量可以用一个 byte 表示 (2 ^ 8 - 1)
  3. 块压缩,每一个块找到最大值要用的 bit,然后放到块头,那每一块要用的 bit 就能算出来了,同时也对块进行了再压缩
    (算法描述参照 Lucene 压缩算法

2 正排索引

2.1 背景

好了,在有了倒排索引之后,通过 term 快速命中 doc的问题是解决了,但是,如果说我的文章中,还有 weight / boost 这种字段,然后我还得在查询的时候根据这些字段进行聚合操作的话,如果用倒排索引会咋样了,就以刚刚那个例子来说,假设一个 doc 里面包含了 tagsboost ,如果说我要这样查(sql like)

select * from test_docs where tags like '%elastic index%' order by boost;

tags 字段建立了倒排的情况下,应该先从倒排找到对应的 posting list,然后找到对应的doc,接着再从doc里面找出所有的boost,对这个boost 排序,返回。这个流程乍看上去没问题,但是数据量大的时候,得遍历大量的doc,最后排序才能返回查询结果,性能比较差。

如果 boost 建立了倒排的话,首先呢,我得遍历整个 boost 的倒排,接着排序,然后再从 tags 查出来的 posting list 跟上面的排序结果进行过滤,才能得出结果,这个操作也是让人头疼的,性能不高不说,要是 boost 索引特别大的时候,效率还低。

所以这个时候就必须得用正排索引了。

2.2 定义

正排索引与传统索引有些像, keydoc_idvalue 是对应的某个字段值

**[doc_1] to [term1]**
**[doc_2] to [term2]**
**[doc_3] to [term3]**

es 中,要启用它比较简单,就是 mapping.properties.字段.doc_value= true
其实 es 会默认创建正排索引的

2.3 优势

当然就是为了在聚合操作的时候能够快速聚合出结果而生的

2.4 生成/更新时机

跟倒排索引一样,在文档被插入的时候,倒排索引就会被生成和更新

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值