https://mp.weixin.qq.com/s/YhpxoPbJNMGPhUfj9PCTZg

es基于什么建立的?

es是基于开源的Lucene库写的一个搜索引擎,Elasticsearch 把操作都封装成了 HTTP 的 API,我们可以通过api快速操作es

概念

索引

用于存放数据的地方,对应mysql的库

类型

用于定义数据结构的,对应mysql的表

keyword类型与text类型的区别

Keyword 类型是不会分词的,直接根据字符串内容建立反向索引,Text 类型在存入 Elasticsearch 的时候,会先分词, 然后根据分词后的内容建立反向索引。

文档

最终的数据,相当于mysql表里的一条记录

分布式原理

Elasticsearch 会对数据进行切分,同时每一个分片会保存多个副本,为了保证分布式环境下的高可用。

倒排索引的原理

正排索引:通过id查找一条记录,通过某个字段查找一条记录,mysql的索引都属于正排索引,与倒排索引正好相反 倒排索引:一般用于全文搜索,首先会将我们的内容进行分词处理,拆分成单独的词(一般会去掉助词这些,我们称为词条或term),然后每个分词,我们都可以直接索引到对应的文档,这就叫倒排索引

词条(term)

索引里最小的存储与查询单元,英文一般来说是一个单词,中文一般是内容用分词后的词

词典 (term dictionary)

是词条的集合,单词词典是指由文档集合中出现过的所有单词构成的字符串集合。单词词典内每条索引项记载单词本身的一些信息以及指向倒排列表的指针 ES 为了能快速查找到 term,将所有的 term 排了一个序,二分法查找。

倒排表(posting list)

一个文档通常是由多个词组成,倒排表记录的是某个词在哪些文档里出现过以及出现的位置。倒排表记录的不仅是文档编号,还存储了词频等信息。

postings list 如果不进行压缩,会非常占用磁盘空间。 联合查询下,如何快速求交并集(intersections and unions)。

①压缩

Frame of Reference:在 lucene 中,要求 postings lists 都要是有序的整形数组。

这样就带来了一个很好的好处,可以通过 增量编码(delta-encode)这种方式进行压缩。

比如现在有 id 列表 [73, 300, 302, 332, 343, 372],转化成每一个 id 相对于前一个 id 的增量值(第一个 id 的前一个 id 默认是 0,增量就是它自己)列表是 [73, 227, 2, 30, 11, 29]。

在这个新的列表里面,所有的 id 都是小于 255 的,所以每个 id 只需要一个字节存储。

倒排文件(inverted file):

所有单词的倒排列表往往顺序地存储在磁盘里的某个文件里,这个文件被称之为倒排文件,倒排文件是存储倒排索引的物理文件。

Term index:

一般词典是存储在内存在,倒排文件存储在磁盘里。但如果词典非常大,就得借助term index了,从数据结构上分类算是一个“Trie 树”, 也就是我们常说的字典树。 这是一种专门处理字符串匹配的数据结构,用来解决在一组字符串集合中快速查找某个字符串的问题。 这棵树不会包含所有的 term,它包含的是 term 的一些前缀(这也是字典树的使用场景,公共前缀)。 通过 term index 可以快速地定位到 term dictionary 的某个 offset,然后从这个位置再往后顺序查找。就想右边这个图所表示的。

lucene 在这里还做了两点优化,一是 term dictionary 在磁盘上面是分 block 保存的, 一个 block 内部利用公共前缀压缩,比如都是 Ab 开头的单词就可以把 Ab 省去。

二是 term index 在内存中是以 FST(finite state transducers)的数据结构保存的。

todo finite是啥

①为了能够快速定位到目标文档,ES 使用倒排索引技术来优化搜索速度,虽然空间消耗比较大,但是搜索性能提高十分显著。

②为了能够在数量巨大的 terms 中快速定位到某一个 term,同时节约对内存的使用和减少磁盘 io 的读取。

lucene 使用 “term index→term dictionary→postings list” 的倒排索引结构,通过 FST 压缩放入内存,进一步提高搜索效率。

③为了减少 postings list 的磁盘消耗,lucene 使用了 FOR(Frame of Reference)技术压缩,带来的压缩效果十分明显。

④ES 的 filter 语句采用了 Roaring Bitmap 技术来缓存搜索结果,保证高频 filter 查询速度的同时降低存储空间消耗。

⑤在联合查询时,在有 filter cache 的情况下,会直接利用位图的原生特性快速求交并集得到联合查询结果,否则使用 skip list 对多个 postings list 求交并集,跳过遍历成本并且节省部分数据的解压缩 cpu 成本。

关于深分页

  1. 如果数据量小(from+size在10000条内),或者只关注结果集的TopN数据,可以使用from/size 分页,简单粗暴
  2. 数据量大,深度翻页,后台批处理任务(数据迁移)之类的任务,使用 scroll 方式
  3. 数据量大,深度翻页,用户实时、高并发查询需求,使用 search after 方式

Scroll和search_after原理基本相同,他们都采用了游标的方式来进行深分页。

这种方式虽然能够一定程度上解决深分页问题。但是,它们并不是深分页问题的终极解决方案,深分页问题「必须避免!!」。

对于Scroll,无可避免的要维护scroll_id和历史快照,并且,还必须保证scroll_id的存活时间,这对服务器是一个巨大的负荷。

对于Search_After,如果允许用户大幅度跳转页面,会导致短时间内频繁的搜索动作,这样的效率非常低下,这也会增加服务器的负荷,同时,在查询过程中,索引的增删改会导致查询数据不一致或者排序变化,造成结果不准确。

Search_After本身就是一种业务折中方案,它不允许指定跳转到页面,而只提供下一页的功能。

Scroll默认你会在后续将所有符合条件的数据都取出来,所以,它只是搜索到了所有的符合条件的doc_id(这也是为什么官方推荐用doc_id进行排序,因为本身缓存的就是doc_id,如果用其他字段排序会增加查询量),并将它们排序后保存在协调节点(coordinate node),但是并没有将所有数据进行fetch,而是每次scroll,读取size个文档,并返回此次读取的最后一个文档以及上下文状态,用以告知下一次需要从哪个shard的哪个文档之后开始读取。

这也是为什么官方不推荐scroll用来给用户进行实时的分页查询,而是适合于大批量的拉取数据,因为它从设计上就不是为了实时读取数据而设计的。 https://mp.weixin.qq.com/s/p5nLhvJq2Myd1G9cvNKlWA