当谈到Elasticsearch时,分词与倒排索引是两个关键的概念,理解它们对于面试中展示对Elasticsearch工作原理的理解至关重要。
「1. 分词(Tokenization):」
分词是将文本分解成一个个单独的词汇单元的过程。在Elasticsearch中,分词是搜索引擎索引和查询的基础。以下是一些关键点:
分词器(Tokenizer):Elasticsearch使用分词器来将文本拆分为词汇单元。常见的分词器包括标准分词器(standard tokenizer)、较为灵活的字母分词器(letter tokenizer)、模式分词器(pattern tokenizer)等。
过滤器(Token Filter):分词后,可以应用过滤器进行词汇单元的转换或过滤。例如,小写过滤器(lowercase filter)将所有单词转换为小写,去除大小写敏感性。
停用词(Stopwords):可以通过停用词过滤器去除一些常见但通常不被用于搜索的词汇,例如“and”、“the”等。
同义词词典(Synonym Token Filter):允许指定同义词,以便搜索时考虑更广泛的相关词汇。
「2. 倒排索引(Inverted Index):」
倒排索引是Elasticsearch中用于实现高效文本搜索的核心数据结构。它的原理如下:
反转索引结构:传统的索引是文档到词汇单元的映射,而倒排索引是词汇单元到文档的映射。每个词汇单元都会记录它在哪些文档中出现。
词项(Term):词汇单元在倒排索引中称为词项。一个词项可以对应多个文档。
倒排列表(Inverted List):每个词项维护一个倒排列表,记录了包含该词项的文档ID列表。
权重(Term Frequency,Document Frequency):除了文档ID列表外,还会记录每个文档中词项的出现频率(TF,Term Frequency)以及包含该词项的文档总数(DF,Document Frequency)。这些信息在搜索时用于计算文档的相关性。
❝「什么是倒排索引?」
❞
倒排索引是一种用于实现文本搜索的数据结构,通过将词汇单元映射到包含它的文档列表,从而实现快速的检索。以下是一个简单的示例来说明什么是倒排索引:假设有三个文档:
文档1: "Elasticsearch is a powerful search engine."
文档2: "Search engines are essential for information retrieval."
文档3: "Searching with Elasticsearch is efficient and fast."建立倒排索引的过程
:
分词:对每个文档进行分词。使用标准分词器,得到以下词汇单元:
文档1: ["Elasticsearch", "is", "a", "powerful", "search", "engine"]
文档2: ["Search", "engines", "are", "essential", "for", "information", "retrieval"]
文档3: ["Searching", "with", "Elasticsearch", "is", "efficient", "and", "fast"]建立倒排索引
:为每个词汇单元建立倒排列表,记录包含该词汇单元的文档ID列表。
"Elasticsearch": 文档1, 文档3
"is": 文档1, 文档3
"a": 文档1, 文档2
"powerful": 文档1
"search": 文档1, 文档2
"engine": 文档1
"engines": 文档2
"are": 文档2
"essential": 文档2
"for": 文档2
"information": 文档2
"retrieval": 文档2
"Searching": 文档3
"with": 文档3
"efficient": 文档3
"and": 文档3
"fast": 文档3
这就是一个简化的倒排索引示例。当执行搜索时,搜索引擎可以快速定位包含搜索词的文档,从而实现高效的文本搜索。例如,搜索 "Elasticsearch" 将直接在倒排列表中找到文档1和文档3,而不需要遍历整个文档集合。这种方式能够极大提高搜索性能。
分段存储是一种在搜索引擎领域常见的思想,它的核心概念是将索引数据划分为小块(段),以便更有效地管理和维护索引。这种思想在Elasticsearch等搜索引擎中得到了广泛应用。以下是关于分段存储的一些要点:
索引划分为段:
概念:索引中的数据被划分为多个独立的段,每个段都是一个独立的索引单元。
好处:将索引划分为段使得索引的维护更加灵活,可以独立地进行增量更新、优化和删除操作,而不必对整个索引进行操作。
实现增量更新:
追加式写入:新的文档或更新会追加到最后一个段中,而不是覆盖原有的数据。
减小刷新代价:在某个段上的变更不需要立即刷新到磁盘,可以在后台异步执行,从而降低写入的代价。
优化与合并:
后台优化:对于搜索引擎来说,由于数据是分段存储的,可以在后台对某个段进行优化,例如合并小的段、删除不再需要的数据等。
减少资源压力:分段存储使得在优化时只需处理少量的数据,降低了系统资源的压力。
提高并发性能:
并发更新:由于数据是分段存储的,多个线程或进程可以并发地更新不同的段,提高了并发性能。
减小锁的粒度:对于搜索和写入操作,锁的粒度更小,降低了竞争和等待的时间。
快速恢复和备份:
粒度控制:分段存储使得在数据恢复或备份时可以更加精确地控制数据的范围,只需处理相关的段。
减小故障影响:当某个段发生故障时,只影响到该段的数据,而不会影响整个索引。
分段存储的思想通过将大的索引划分为小的、独立的段,提高了搜索引擎的维护、优化和并发性能,同时也使得数据的备份和恢复更加灵活和高效。在Elasticsearch等搜索引擎中,这种分段存储的策略有助于实现快速、可靠的文本搜索和检索。
❝假设我们有一个包含文档的索引,每个文档有两个字段:标题(Title)和内容(Content)。现在,我们将这个索引划分为两个段(Segment)。
❞
初始状态:
「Segment 1:」
Doc1: Title - "Elasticsearch Basics", Content - "Introduction to Elasticsearch."
Doc2: Title - "Search Algorithms", Content - "Algorithms used in search engines."
「Segment 2:」
Doc3: Title - "Indexing Techniques", Content - "Techniques for efficient data indexing."
Doc4: Title - "Query Optimization", Content - "Optimizing queries for better search performance."
现在,考虑以下操作:
「新增文档:」 我们要添加一个新文档。
新增 Doc5: Title - "Data Analysis with Elasticsearch", Content - "Analyzing data using Elasticsearch."
这个新增的文档可以被追加到任意一个现有的段中,而不需要更新整个索引。
「更新文档:」 我们要更新一个现有文档的内容。
更新 Doc2: Content - "Advanced algorithms used in search engines."
由于我们采用追加式写入,更新操作只需要在相应段中追加新的信息,而不是覆盖原有的数据。
「删除文档:」 我们要删除一个文档。
删除 Doc3
删除文档时,只需将其在相应段中标记为删除,而不是立即清除整个文档。清理可以在后台进行。
「优化与合并:」 定期进行索引的优化与合并操作。
合并 Segment 1 和 Segment 2,以减少小的段的数量并进行性能优化。
这些操作都可以在后台异步进行,而不会对整个索引产生较大影响。每个段都是独立的,这使得操作的粒度更小,提高了系统的并发性能,减小了锁的竞争。
段合并是搜索引擎中维护索引的一个重要操作,它涉及到将多个小的段(segments)合并为一个更大的段,以提高索引的性能和效率。以下是关于段合并策略的一些思想:
合并触发条件:
小型段合并:当索引中的小型段数量达到一定阈值时,可以触发小型段的合并。这样可以减少段的数量,降低搜索时需要扫描的段的数量,提高搜索性能。
删除标记合并:当一个段中有较多被标记为删除的文档时,可以触发合并操作,清理被删除的文档,减小索引的存储空间。
定期合并:定期执行合并操作,确保索引的整体性能得到维护。
合并策略:
大小合并:将相邻的小型段合并成一个更大的段。这可以降低段的数量,减少搜索时需要扫描的段数,提高搜索性能。
删除标记清理:清理那些包含大量被标记为删除的文档的段,释放存储空间。
后台异步合并:尽量避免在搜索高峰期进行合并,而是将合并操作放置在系统负载较低的时候进行,以减小对搜索性能的影响。
资源和性能权衡:
磁盘空间:合并可以释放磁盘空间,对于长时间运行的索引,这是一个重要的考虑因素。
性能影响:合并操作可能会对系统性能产生一定的影响,因此需要在平衡性能和资源利用之间进行权衡。
持续监控:实时监控索引的状态,根据实际需求调整合并的触发条件和策略。
预防碎片化:
合并紧凑的段:防止索引碎片化,合并紧凑的段有助于提高搜索性能。
避免频繁合并:过于频繁的合并可能会引入性能问题,因此需要合理设置触发条件和合并频率。
合并策略的灵活性:
可配置性:搜索引擎通常提供了合并策略的可配置选项,以满足不同场景和需求的要求。
动态调整:能够动态调整合并策略,根据实际运行情况进行优化。
理解合并策略的思想是搜索引擎优化和性能调优的关键之一。通过合理设置触发条件、选择合适的合并策略以及动态调整参数,可以保持索引的健康状态,提高搜索性能,降低系统的负载。
假设我们有一个简单的索引,包含三个小型段(Segment)。
「Segment 1:」
Doc1: Title - "Elasticsearch Basics", Content - "Introduction to Elasticsearch."
「Segment 2:」
Doc2: Title - "Search Algorithms", Content - "Algorithms used in search engines."
「Segment 3:」
Doc3: Title - "Indexing Techniques", Content - "Techniques for efficient data indexing."
现在,我们考虑触发和执行段合并的一些场景:
小型段合并:
场景:每个段都相对较小,触发合并以减少段的数量。
操作:将 Segment 1 和 Segment 2 合并成一个新的段,得到新的 Segment 4。
Segment 4:
Doc1: Title - "Elasticsearch Basics", Content - "Introduction to Elasticsearch."
Doc2: Title - "Search Algorithms", Content - "Algorithms used in search engines."
效果:减少了段的数量,提高了搜索性能。
删除标记合并:
场景:Segment 3 中包含了大量被标记为删除的文档。
操作:触发合并操作,清理 Segment 3 中被标记为删除的文档,得到新的 Segment 5 [因为只包含doc3 所以5 应该是空的]。
效果:释放了存储空间,减小了索引的体积。
定期合并:
场景:在系统负载较低的时候,定期执行合并操作。
操作:将 Segment 4 和 Segment 5 合并为一个更大的段,得到新的 Segment 6。 Segment 6:
Doc1: Title - "Elasticsearch Basics", Content - "Introduction to Elasticsearch."
Doc2: Title - "Search Algorithms", Content - "Algorithms used in search engines."
效果:保持索引的整体性能,避免了在高峰期进行合并的影响。
合并紧凑的段:
场景:Segment 6 比较紧凑,可以进一步提高搜索性能。
操作:触发合并操作,将 Segment 6 优化为一个更大且更紧凑的段,得到新的 Segment 7。 Segment 7:
Doc1: Title - "Elasticsearch Basics", Content - "Introduction to Elasticsearch."
Doc2: Title - "Search Algorithms", Content - "Algorithms used in search engines."
效果:提高了搜索性能,减小了碎片化。
TF-IDF(Term Frequency-Inverse Document Frequency)算法是一种常用于信息检索和文本挖掘的技术,用于衡量一个词在文档集合中的重要性,从而评估两个文本之间的相似度。
下面是对TF-IDF的详细描述:
词频(Term Frequency - TF):
定义:词频表示某个词在文档中出现的频率。它是一个针对单个文档的度量。
计算公式: TF(t,d)=词t在文档d中出现的次数/文档d的总词数
特点:TF考虑了一个词在文档中的重要性,但没有考虑该词在整个文档集合中的分布情况。
逆文档频率(Inverse Document Frequency - IDF):
定义:逆文档频率表示一个词在整个文档集合中的重要性。它是一个针对词汇的全局度量。
计算公式: IDF(t,D)=log(文档集合D的总文档数/包含词t的文档数+1)
特点:IDF衡量了一个词的稀缺性,如果一个词在很多文档中都出现,那么它的IDF值较低。
TF-IDF值:
定义:TF-IDF值是将词频和逆文档频率相结合的度量,用于表示一个词在一个文档中的重要性。
计算公式: TF-IDF(t,d,D)=TF(t,d)×IDF(t,D)
特点:通过TF和IDF的乘积,TF-IDF强调了一个词在当前文档中的重要性,同时考虑了它在整个文档集合中的分布情况。
文本相似度计算:
通过TF-IDF算法,我们可以计算文档之间的相似度,进而在信息检索、文本分类等应用中有效地衡量文本之间的关联性。
Elasticsearch的写索引逻辑是基于分布式架构和倒排索引的原理。以下是简要的Elasticsearch写索引的逻辑:
文档准备:
用户准备要索引的文档,包含文档的字段和内容。
分析器处理:
文档进入分析器(Analyzer)阶段,分析器负责将文档拆分成词汇单元(terms)。
倒排索引的生成:
对于每个词汇单元,Elasticsearch创建倒排索引。
每个倒排索引条目包含词汇单元和指向包含该词汇单元的文档的引用。
分片分配:
文档被分配到不同的分片(shard)中,确保索引的水平扩展性和分布式特性。
文档的主副本写入:
文档的主分片和副本分片(replica)之间协调写入。主分片接收写请求,并将操作传播到其副本分片。
写请求首先被路由到文档的主分片,确保主分片始终处理写入请求。
刷新(Flush):
写入请求在主分片和副本分片上执行,并存储在内存中的索引缓冲区。
定期或在缓冲区达到一定大小时,进行刷新操作,将内存中的索引数据刷写到磁盘。
写入确认:
写入请求在主分片和副本分片上成功执行后,向客户端返回写入确认。
并发控制:
Elasticsearch使用乐观并发控制,通过版本号来确保在多个写入操作同时进行时的一致性。
每个文档都有一个版本号,每次写入都会递增版本号。
数据同步:
主分片和副本分片之间进行数据同步,确保高可用性和故障恢复。
异步地进行副本分片的同步,允许主分片在写入期间继续服务。
实时搜索(Near Real-Time Search):
写入的文档在刷新之后就能够被搜索,提供实时的搜索体验,尽管实际上刷新是定期进行的。
这是一个简要的Elasticsearch写索引的流程,涵盖了从文档准备到倒排索引的生成,再到分片分配、分片同步等关键步骤。这些步骤保证了Elasticsearch在大规模数据和高并发写入的环境中能够提供高性能、高可用性和分布式处理的能力。
❝在Elasticsearch中,客户端可以使用集群的元数据来获取关于索引分片(shard)的信息,包括哪个分片是主分片。以下是一些方法: 集群状态 API:
客户端可以使用Elasticsearch的Cluster State API来获取有关集群状态的信息,包括每个索引的分片分配情况。
通过访问/_cluster/state端点,可以检索有关索引、分片和节点的详细信息。在这些信息中,您可以找到每个索引的主分片和副本分片的分布情况。
索引元数据:
在索引的元数据中,有有关分片的信息,包括主分片和副本分片的分布情况。
使用/_cat/indices或/_cat/shards端点,您可以获取关于索引及其分片的基本信息。
索引别名:
在索引别名(Index Alias)中,您可以指定哪个分片是主分片。
利用索引别名可以将主分片和副本分片的实际物理分配情况与别名进行解耦,使得在进行分片迁移等操作时更加灵活。
Java客户端API:
❞
如果您使用Elasticsearch官方提供的Java客户端,您可以使用ClusterStateRequest和ClusterStateResponse API来获取有关集群状态的信息,包括索引的分片分布情况。
❝下面是一个使用curl来检索索引分片信息的示例:
❞curl -XGET "http://your-elasticsearch-node:9200/_cluster/state?pretty"
通过这些方式,客户端可以动态地了解到集群的状态和索引的分片分布情况,从而知道哪个分片是主分片。这种灵活性使得客户端能够适应集群拓扑结构的变化,以确保对分片的写入请求被正确路由到主分片。
Elasticsearch中搜索数据的过程涉及多个步骤,包括搜索请求的解析、分布式查询的执行、分片的合并等。以下是详细的搜索数据过程:
「1. 搜索请求的接收与解析:」
客户端发送搜索请求到Elasticsearch集群的任意节点。
请求包含查询语句、过滤条件、排序规则等。
请求被接收后,节点会解析查询,并将请求路由到包含相关索引的分片。
「2. 分片的选择与路由:」
Elasticsearch中的索引被分成多个分片,每个分片可以存在于不同的节点上。
查询路由器(Query Router)根据查询的内容和索引的分片分布情况,确定哪些分片包含需要搜索的数据。
查询被路由到包含目标数据的主分片上,同时也可能涉及到副本分片。
「3. 分片级别的搜索操作:」
主分片和副本分片上都会执行搜索操作,生成局部的搜索结果。
搜索操作在倒排索引上执行,筛选出与查询条件匹配的文档。
「4. 分片的合并与排序:」
各个分片返回的局部结果会被合并到协调节点上。
协调节点负责对合并的结果进行全局排序,以满足排序规则。
「5. 结果返回给客户端:」
合并和排序后的结果集被返回给客户端,包括匹配的文档、相关性分数、排序等信息。 以下是一个简单的示例:
假设有一个包含产品信息的索引,有两个主分片和一个副本分片。现在,客户端发送一个查询请求,要求查找价格在100到200之间的商品,并按照销量降序排序。
「1. 搜索请求:」
客户端发送请求:GET /products/_search?q=price:[100 TO 200]&sort=sales:desc
「2. 路由与分片选择:」
查询路由器确定与产品索引相关的分片。
请求被路由到包含相关数据的主分片和副本分片上。 「3. 分片级别搜索:」
主分片和副本分片上执行搜索操作,查找价格在100到200之间的商品。
「4. 分片结果合并:」
每个分片返回局部结果。
协调节点收到所有分片的结果后,合并它们。
「5. 排序与结果返回:」
协调节点对合并的结果进行全局排序,按照销量降序。
最终结果返回给客户端。
深翻页(Deep Pagination)是指在搜索结果集中获取大量数据时,需要请求大量的文档,可能导致性能问题的情况。这是因为在深层次的页数上,Elasticsearch需要跨越大量的分片来检索文档,从而增加了查询的开销和响应时间。
分布式检索:在深翻页时,涉及到多个分片的数据检索,这可能导致每个分片都要处理大量的文档,产生较高的查询成本。
数据的实时性:如果在搜索期间索引中的文档发生变化,可能会影响搜索结果的一致性。
「1. 使用 Scroll API:」
Scroll API是Elasticsearch提供的用于处理大量数据的机制。它允许客户端获取一个持续保持打开的搜索上下文,从而在多次请求中获取更多的结果。
Scroll API通过维护一个快照来实现,而不是依赖于实时的索引状态。这有助于保持数据的一致性。
「2. 游标(Cursor)的使用:」
游标是Scroll API的一部分,用于标识搜索上下文。客户端通过在请求中包含游标来获取下一页的结果。
游标的使用可以有效地获取大量数据,同时保持性能。
「3. 提高分页大小:」
增加每页返回的文档数量可以减少深翻页的请求数量,从而降低查询成本。但这需要注意每页文档数量的合理设置,以兼顾性能和内存使用。
# 使用 Scroll API 进行深翻页
POST /index/_search?scroll=1m
{
"size": 100, # 每次获取的文档数量
"query": {
"match_all": {}
}
}
上述请求中,scroll=1m
表示每次请求后的搜索上下文将保持1分钟。客户端可以使用这个上下文来获取下一页的结果。通过 Scroll API,可以有效地处理大量数据而不影响性能。
解决深翻页问题的关键是利用Scroll API和游标,以及适当调整每页文档数量。这样可以有效地处理大量数据,同时保持查询的性能和一致性。
关注公众号 “洪都新府笑颜社”,发送 “面试题” 即可免费领取一份超全的面试题PDF文件!!!!