Elasticsearch 是一个开源的搜索引擎,建立在一个全文搜索引擎库 Apache Lucene 基础之上。Elasticsearch 也是使用 Java 编写的,它的目的是使全文检索变得简单,通过隐藏 Lucene 的复杂性,取而代之的是提供一套简单一致的 RESTful API。
ES 在建立索引的时候,一方面可以建立倒排索引供搜索用,另一方面可以建立 Doc Values,供排序、聚合、过滤等操作使用。倒排索引是搜索引擎的核心,ES 通过 Lucene 的倒排索引技术实现了比关系型数据库更快的过滤。
1. 倒排索引
倒排索引是单词(term or token)到文档 id 的关联关系,每个记录索引的字段都有自己的倒排索引。倒排索引主要包含单词词表(Term Dictionary)和倒排列表(Posting List),通过将文档内容进行分词,即可得到单词词表(Term Dictionary):
单词(term or token) | 文档 id |
---|---|
ES | 10001,10003 |
最 | 10001 |
流行 | 10001 |
搜索引擎 | 10001,10002 |
发展 | 10002 |
历史 | 10002 |
权威 | 10003 |
指南 | 10003 |
单词词表一般以 B+ Tree 的形式缓存在服务器内存中,下图是一个单词词表的示例,单词词表的每个叶子节点都记载了指向倒排列表的指针,比如单词 “搜索引擎” 指向了 10001 和 10002。
倒排列表记录了单词对应的文档集合,由倒排项(Posting)组成,Posting 主要包含文档 id、单词频率(TF,记录该单词在该文档中的出现次数,用于后续相关性算分)、位置(Position,记录单词在文档中的分词位置)、偏移(Offset,记录单词在文档的开始和结束位置)。倒排列表顺序的存储在磁盘文件中,这个文件被称为倒排文件,倒排文件是存储倒排索引的物理文件。
最后我们看下倒排索引的检索流程:
- 通过单词词表检索单词,拿到倒排列表的指针,这一步在内存中完成,非常快;
- 通过倒排列表的指针获取单词对应的文档集合;
- 通过 Doc Values 查询文档 id 的完整内容,返回用户最终结果。
为什么 ES/Lucene 的检索比 MySQL 快?
- MySQL B+tree 索引是存储在磁盘中的,检索时索引并不能直接找到行,而是先找到行所在的页,通过把整页读入内存,再在内存中查找,故 MySQL 检索时需要若干次的随机访问磁盘操作,索引的 B+tree 高度一般为 2-4 层,查找记录时最多需要 2-4 次 IO;而 ES 的倒排索引是存储在内存中的,通过倒排索引拿到倒排列表的指针,再去磁盘上找 term,磁盘的随机访问次数要比 MySQL 少,性能自然就比 MySQL 要好;
- 基于分词后的全文检索,ES 分词后,每个 term 都可以利用 FST 高速找到倒排索引的位置,并迅速获取文档 id 列表;而对于 MySQL 而言完全是灾难,MySQL 检索中间的词只能全表扫。
2. Doc Values
当你对一个字段进行排序时,ES 需要访问每个匹配到的文档得到相关的值。倒排索引的检索性能是非常快的,但是在字段值排序时却不是理想的结构。
- 在搜索的时候,我们能通过搜索关键词快速得到结果集;
- 当排序的时候,我们需要倒排索引里面某个字段值的集合。换句话说,我们需要 转置 倒排索引。
转置 结构在其他系统中经常被称作 列存储。实质上,它将所有单字段的值存储在单数据列中,这使得对其进行操作是十分高效的,例如排序。
在 ES 中,Doc Values 就是一种列式存储结构,默认情况下每个字段的 Doc Values 都是激活的,Doc Values 是在索引时创建的,当字段索引时,ES 为了能够快速检索,会把字段的值加入倒排索引中,同时它也会存储该字段的 Doc Values。
ES 中的 Doc Values 常被应用到以下场景:
- 对一个字段进行排序;
- 对一个字段进行聚合;
- 某些过滤,比如地理位置过滤;
- 某些与字段相关的脚本计算。
因为文档值被序列化到磁盘,我们可以依靠操作系统的帮助来快速访问。当 working set 远小于节点的可用内存,系统会自动将所有的文档值保存在内存中,使得其读写十分高速; 当其远大于可用内存,操作系统会自动把 Doc Values 加载到系统的页缓存中,从而避免了 jvm 堆内存溢出异常。
参考:
https://www.elastic.co/guide/cn/elasticsearch/guide/current/inverted-index.html
https://www.elastic.co/guide/cn/elasticsearch/guide/current/docvalues-intro.html