1 推荐文章
2万字详解,吃透 Elasticsearch
2 什么是倒排索引,为什么这么叫?
倒排索引(Inverted Index)是一种为快速全文搜索而设计的数据结构。它被广泛应用于搜索引擎,其中 Elasticsearch(简称 ES)是一个主要的使用者。
2.1 什么是倒排索引?
传统的索引(例如书的索引或数据库的B-Tree索引)是“正向”的:它们从“文档到词汇项”的映射。换句话说,你查找文档,然后列出文档中的词汇项。
相反,倒排索引从“词汇项到文档”的映射。这意味着,对于索引中的每一个唯一的词汇项,都有一个相关的文档列表,这些文档包含该词汇项。
例如,考虑以下简单文档集:
1. Apple is tasty.
2. Banana is tasty.
3. Apple and banana are fruits.
一个简单的倒排索引可能是:
Apple -> 1, 3
Banana -> 2, 3
tasty -> 1, 2
...
2.2 为什么叫“倒排索引”?
这个名称的由来是因为它与“正向索引”相对立。正向索引直接映射文档到其中的词汇项,而倒排索引则相反,映射词汇项到包含它们的文档。
2.2 为什么使用倒排索引?
倒排索引尤其对于全文搜索很有用,因为:
- 速度:它允许快速查找包含特定词汇项的文档。
- 空间效率:只为唯一的词汇项保存索引,并与相关的文档关联,这避免了重复。
- 排名和相关性:使用倒排索引,可以很容易地为搜索结果进行排名,基于词频、文档频率等。
Elasticsearch 作为一个分布式搜索和分析引擎,大量使用倒排索引来实现其高速和高效的文本搜索功能。
3 ES的内部FST索引
Elasticsearch 为了能快速找到某个 Term,先将所有的 Term 排个序,然后根据二分法查找 Term,时间复杂度为 logN,就像通过字典查找一样,这就是 Term Dictionary。
现在再看起来,和前缀树非常类似, 共享前缀,但是FST还能够共享后缀。但是如果 Term 太多,Term Dictionary 也会很大,放内存不现实,于是有了 Term Index。
就像字典里的索引页一样,A 开头的有哪些 Term,分别在哪页,可以理解 Term Index是一棵树。
这棵树不会包含所有的 Term,它包含的是 Term 的一些前缀。通过 Term Index 可以快速地定位到 Term Dictionary 的某个 Offset,然后从这个位置再往后顺序查找。
在内存中用 FST 方式压缩 Term Index,FST 以字节的方式存储所有的 Term,这种压缩方式可以有效的缩减存储空间,使得 Term Index 足以放进内存,但这种方式也会导致查找时需要更多的 CPU 资源。
对于存储在磁盘上的倒排表同样也采用了压缩技术减少存储所占用的空间。
3.1 ES中什么是FST
在Elasticsearch中,FST(Finite State Transducer)是一个数据结构,用于优化和压缩Term Dictionary。它是一种特殊的有限状态机,不仅可以表示键(在Elasticsearch的场景中,键通常是词项或term),还可以同时表示与该键关联的值。
以下是关于Elasticsearch中FST的一些核心点:
-
压缩:FST非常适合于压缩词典,因为它可以共享前缀和后缀。考虑到词汇中很多词项有共同的前缀和后缀,这种压缩方法是非常高效的。
-
快速查找:除了存储优势,FST还提供了快速的查找能力。当用户查询一个词项时,Elasticsearch可以迅速通过FST找到与该词项对应的元数据或其他相关信息。
-
存储键和值:与传统的前缀树(trie)不同,FST不仅存储词项(键)还可以存储与之关联的值。在Elasticsearch中,这些值可能是关于词项的元数据,例如其在倒排索引中的位置。
-
减少内存使用:由于其压缩特性,FST能够大大减少Term Dictionary在内存中的占用。这使得更多的数据可以被缓存在内存中,从而提高查询性能。
总之,在Elasticsearch中,FST是一个用于表示和压缩Term Dictionary的高效数据结构,使得Term Dictionary的存储更加高效,查询更加快速。
3.2 为什么前缀树也是一种有限状态机(FSM)
确切地说,前缀树(Trie)并不是FST(Finite State Transducer),但它们都属于有限状态机的范畴。FST是有限状态机的一个扩展,可以同时表示键和值。然而,Trie和FST都是基于有限状态机的数据结构,并且都能够有效地表示键的集合,这也是它们之间的相似之处。
让我们深入了解一下它们之间的区别和联系:
-
前缀树(Trie):
- Trie是一个树形数据结构,专门用于存储一个字符串集合。
- 它通过共享公共前缀来优化存储空间。
- 每个节点代表一个字符,从根节点到叶节点的路径表示一个字符串。
- Trie不直接存储与键关联的值,但可以将值附加到叶节点或某些特定节点上。
-
有限状态转换器(FST):
- FST是有限状态机的一个扩展,它可以表示状态之间的转换(即键)以及与每个转换关联的输出值。
- FST不仅可以共享前缀,还可以共享后缀,从而进一步压缩数据。
- 在FST中,一个状态可以代表一个字符串的前缀或后缀,或者代表一个完整的字符串。
- FST直接在其结构中表示与键关联的值。
由于FST能够共享前缀和后缀,并直接表示键和值,它比Trie更加灵活和高效,特别是在表示大量数据时。
简而言之,虽然Trie和FST都是基于有限状态机的数据结构,并有共享前缀的特性,但FST提供了更多的功能和更高的存储效率。在某些上下文中,如Elasticsearch的实现,FST由于其压缩和表示能力而被优先选择。
3.3 前缀树和FST的差异
FST和前缀树(Trie)在共享前缀的策略上是相似的。它们都会通过为字符串的每个字符建立路径来存储信息。在这种结构中,相同前缀的词汇会共享存储路径,直到它们的字符开始有所不同为止。
不过,FST与传统的前缀树有以下主要区别:
-
后缀共享:除了前缀共享,FST还试图共享后缀。这意味着,与前缀树可能会为相似的词后缀存储多次不同,FST会尝试重用和共享这些后缀,进一步减少所需的存储空间。
-
存储值:FST还能存储与键相关联的值。这使得FST可以被视为键-值映射,其中键是词项,值可以是任何相关数据,如词项在倒排索引中的位置。在Elasticsearch的上下文中,这通常是词项的元数据。
-
压缩:FST还采用了多种压缩技巧,以减少实际的存储需求。
总体而言,虽然FST和前缀树在共享前缀的基本策略上相似,但FST包含了更多的优化和扩展功能,使其在某些应用中更为高效。
3.4 FST是如何做到后缀共享的
让我们来详细讨论后缀共享。
共享后缀在复杂的词集中更常见。考虑一组更复杂的词,例如:["attempt", "attempts", "attempted", "attempting", "doing", "seated"]
。
在这些词中,我们看到了前缀"attempt"
的共享,但还有后缀"s"
, "ed"
, 和"ing"
。
为了构建一个考虑到后缀共享的FST:
- 从开始状态,我们首先连接到字符
a
,然后依次到t
,t
,e
,m
,p
。 - 到此为止,我们已经表示了词
"attempt"
。 - 从
"attempt"
的最后一个字符"t"
,我们可以分叉到三个不同的后缀:"s"
表示"attempts"
"ed"
表示"attempted"
"ing"
表示"attempting"
- 为"doing"建立树中节点时,会依次向下层建立d->o节点,然后o节点的指针会指向3中的ing节点,这时ing实现了后缀共享
- 同理,为seated词条建立4个层级的节点,ed直接复用3中早已经建立的后缀
每个后缀都在FST中只存储一次,从而实现了后缀的共享。
共享前缀和后缀是FST高效性的关键。在真实的数据集中,这种共享可以极大地压缩存储需求,特别是当词集包含许多变种和派生词时。
可以如下图所示,看到上面这个例子中的后缀共享方法
3 使用ES查找流程
3.1 先弄清楚ES的主要数据结构
3.2 使用ES作为搜索引擎的查找流程
Elasticsearch(ES)是一个基于Lucene库的搜索引擎。当你在ES上执行搜索时,它会经过以下主要步骤:
-
查询解析:
- 用户发出查询请求,通常是一个JSON结构。
- ES解析这个请求,将查询语句转换为适合Lucene处理的内部格式。
- 这包括分析查询字符串、识别查询类型(如
match
,term
,range
等)和其他可能的查询参数。
-
文本分析:
- 如果查询涉及到文本搜索,那么输入的文本会被分析。
- 分析器(Analyzer)会将文本拆分为单个的词条也称分词(tokens),并可能执行其他操作,如小写化、去除停用词或应用词干提取。
- 这个过程确保查询的词条和索引中的词条在相同的格式下。
-
查找Term Dictionary:
- 对于每个词条,ES首先会查找词汇表(Term Dictionary)来找到该词条的元数据和位置信息。这通常使用FST(Finite State Transducer)来优化查找速度。
-
倒排索引查找:
- 一旦找到词条,ES会查阅倒排索引来找出包含该词条的所有文档ID。
- 对于多词条查询,ES会为每个词条执行此操作,并根据查询类型(例如布尔查询)组合结果。
-
打分和排序:
- 使用BM25算法(默认)或其他相关性算法,ES为每个匹配的文档计算一个分数。
- 这个分数表示文档与查询的相关性。
- 所有匹配的文档根据分数进行排序。
-
获取文档:
- ES从主索引中获取与最高分匹配的文档的完整版本。
- 可以根据查询的需要返回所有或部分字段。
-
高亮和聚合(如果请求):
- 如果查询请求中包含高亮指令,ES会标记查询匹配的部分。
- 如果请求中包含聚合,ES会根据指定的字段和聚合类型计算统计信息。
-
返回结果:
- ES构建一个JSON响应,其中包含匹配的文档、分数、可能的高亮和聚合结果,并将其返回给客户端。
这就是ES的基本搜索流程。当然,实际操作可能会涉及更多的复杂性,例如分片的处理、多节点查询和结果合并、过滤器的应用等。
3.3.1 一般的浏览器的搜索引擎就是使用ES的吗?
不是。浏览器中的主要搜索引擎如Google、Bing、Yahoo等都使用的是他们自己的专有搜索技术。Elasticsearch通常用于网站内的搜索、日志分析、实时应用监控和其他场景,而不是为全球范围的网页搜索设计的。
3.3.2 ES返回的结果是docid对应的文档的元数据信息吗?还是文档内容?
ES返回的结果既可以是文档的元数据,也可以是文档的实际内容。当执行搜索查询时,你可以指定需要哪些字段作为返回结果。如果没有指定,那么默认情况下,ES会返回整个文档。
3.3.3 ES的BM25算法是什么?
BM25是一种现代的、基于概率的文档排序算法,被认为比传统的TF-IDF方法更有效。BM25考虑了词条频率(TF)和逆文档频率(IDF)来计算文档的相关性分数,但与TF-IDF相比,它对高频词条有一个上限,这可以避免某些词条过度影响文档的相关性得分。
3.3.4 获取文档: 这里的主索引是什么索引和FST的区别
当我们提到Elasticsearch的“主索引”,我们是指存储文档的数据结构, 类似于OS中的索引节点方式,主索引节点只存储文档的元数据信息和一个指向文档位置的指针。在这里,文档是按照其ID存储和检索的。FST是用于存储和查找词汇表中词项的数据结构。简单来说,主索引关注的是整个文档,而FST关注的是文档中的词条。
3.3.5 为什么仅仅只返回部门字段,这里的字段包含什么?
在某些场景下,为了提高效率和减少网络传输,你可能只对文档的某些字段感兴趣,而不是整个文档。例如,如果你的文档是一个包含多个字段的用户资料,但你只需要姓名和电子邮件地址,那么可以指定只返回这两个字段。在ES查询时,可以使用"_source"参数指定返回哪些字段。这里的“字段”指的是文档内的任何属性,例如标题、作者、发布日期等。
4 一般的浏览器的搜索引擎就是使用ES的吗
不是。浏览器中的主要搜索引擎如Google、Bing、Yahoo等都使用的是他们自己的专有搜索技术。Elasticsearch通常用于网站内的搜索、日志分析、实时应用监控和其他场景,而不是为全球范围的网页搜索设计的。
5 为什么ES是准实时的
为什么ES是准实时的
6 ES怎么用
ElasticSearch在项目中具体怎么用?
6.1 Elasticsearch是否单独建一个项目,作为全文搜索使用,还是直接在项目中直接用?
这取决于你的需求和项目的复杂性。很多公司和项目都选择将Elasticsearch作为一个单独的服务或微服务运行,与主应用分开。这样做的好处是,它可以独立于主应用进行扩展,维护和更新,从而实现了解耦。但如果你的应用较小,或者你只是想快速地进行原型开发,那么可以直接在应用中集成Elasticsearch。
6.2 新增数据时,插入到mysql中,需不需要同时插入到es中?
如果你依赖Elasticsearch进行搜索或其他需要快速读取的操作,当你在MySQL中插入数据时,你也应该将这些数据插入到Elasticsearch中。这样,你可以确保当用户尝试搜索新添加的内容时,Elasticsearch的索引是最新的。有多种方法可以做到这一点,例如使用日志文件同步或使用工具和库,如Logstash或Debezium。
当你在MySQL中新增数据并希望这些数据在Elasticsearch中可搜寻,你需要确保这些数据也被同步到Elasticsearch。关于数据同步的触发方式,有几种常见的方法:
-
客户端发起的双重写入: 当应用程序(客户端)在MySQL中插入数据后,它也可以负责将同样的数据发送到Elasticsearch。这种方法的挑战在于确保两边的写入都成功,否则可能出现数据不一致的情况。
-
MySQL的binlog监听与同步: 使用如Debezium或Logstash等工具监听MySQL的binlog(二进制日志),当检测到有新的数据变更时,自动将变更推送到Elasticsearch。这种方式相对客户端双重写入更为可靠,因为它确保了即使应用程序出现问题,数据也能被正确同步。
-
周期性的数据同步: 设定一个定时任务,例如每隔几分钟,检查MySQL的新数据,并同步到Elasticsearch。这适用于数据实时性要求不高的场景。
-
触发器: 在MySQL中设置触发器,当有新的数据插入或变更时,触发某些操作进行同步。但这种方法可能影响MySQL的性能,并且需要额外的工作来确保同步的成功。
总的来说,选择哪种方法取决于你的具体需求和系统的架构。但通常,监听binlog的方式因其相对的可靠性和低延迟而被广泛采用。
6.3 搜索时直接返回es搜索的结果,还是需要根据es的结果中的id,回mysql中重新查一遍?
这同样取决于你的应用和需求。在某些场景下,用户可能只需要从Elasticsearch中获取的元数据,所以直接返回Elasticsearch的结果就足够了。在其他情况下,可能需要返回更多详细的信息,那么可以使用Elasticsearch提供的ID在MySQL中进行查找。
关于使用MySQL:Elasticsearch主要是为搜索和分析设计的,并不是为了替代传统的关系数据库。MySQL(或其他RDBMS)提供了事务性、完整性、备份和恢复等功能,这些都是Elasticsearch不擅长或不支持的。而Elasticsearch主要针对的是大量数据的快速搜索和分析。所以,一般来说,MySQL用作主要的数据存储,而Elasticsearch用作搜索和分析工具。