Elasticsearch是一个强大的开源分布式搜索和数据分析引擎,目前被国内多家互联网公司采用,包括携程、滴滴、今日头条、饿了么、360安全、小米、vivo等。图片来自Pexels除了搜索,结合Kibana、Logstash、Beats,ElasticStack还广泛应用于大数据近实时分析领域,包括日志分析、指标监控、信息安全等领域。它可以帮助您探索海量结构化和非结构化数据,按需创建可视化报告,为监控数据设置警报阈值,甚至可以利用机器学习技术自动识别异常情况。今天,我们先从上到下介绍一下ElasticSearch的底层工作原理,并尝试回答以下问题:为什么我的搜索*foo-bar*匹配不到foo-bar?为什么添加更多文件会压缩索引(Index)?为什么ElasticSearch占用内存大?图ElasticSearchelasticsearch版本:elasticsearch-2.2.0。①云上的集群如下图所示:②集群中的方框云中的每个白色方框代表一个节点——Node。③节点直接连接一个或多个节点,多个绿色小方块组合起来组成一个ElasticSearch索引。④索引中的小方块在一个索引下,分布在多个节点的绿色小方块称为分片——Shard。⑤Shard=LuceneIndex一个ElasticSearchshard本质上就是一个LuceneIndex。Lucene是一个全文搜索库(还有许多其他形式的搜索库),而ElasticSearch是建立在Lucene之上的。故事的其余大部分实际上是ElasticSearch如何在Lucene之上工作。GraphicalLuceneMiniindex:Segment在Lucene中有很多小的Segment,我们可以把它们看作是Lucene内部的mini-index。Segment内部Segment内部有很多数据结构,如上图:InvertedIndexStoredFieldsDocumentValuesCache最重要的InvertedIndex如下图:InvertedIndex主要包括两部分:一个有序的数据字典词典(包括单词Term及其出现频率)。对应于单词Term的帖子(即单词存在的文件)。我们在搜索的时候,首先对搜索内容进行分解,然后在字典中查找对应的词条,从而找到与搜索相关的文件内容。①查询“thefury”如下图:②自动补全(AutoCompletion-Prefix)如果要查找以字母“c”开头的字母,可以简单地使用二分查找(BinarySearch)在InvertedIndex表中查找,例如“选择”、“到来”这样的词(Term)。③昂贵的搜索如果要查找所有包含字母“our”的词,那么系统会扫描整个InvertedIndex,这是非常昂贵的。在这种情况下,如果我们要优化,那么我们面临的问题就是如何生成一个合适的Term。④问题的转化如下图所示:对于上面的问题,我们可能有几种可行的解法:*后缀→xiffus*,如果我们想用后缀作为搜索条件,可以对Term做逆向处理.(60.6384,6.5017)→u4u8gyykk,对于GEO位置信息,可以转化为GEOHash。123→{1-百,12-十,123},对于简单的数字,可以为它生成多种形式的Term。⑤解决拼写错误Python库生成一个包含单词拼写错误信息的树状状态机来解决拼写错误问题。⑥StoredField字段搜索当我们要查找包含特定标题内容的文件时,InvertedIndex并不能很好的解决这个问题,所以Lucene提供了另一种数据结构StoredFields来解决这个问题。本质上,存储字段是一个简单的键值对。默认情况下,ElasticSearch存储整个文件的JSON源。⑦DocumentValuesForsortingandaggregation即使如此,我们发现上面的结构还是解决不了如:sorting,aggregation,facet,因为我们可能要读取很多不必要的信息。因此,另一种数据结构解决了这个问题:DocumentValues。这种结构本质上是一种列式存储,对同类型数据的存储结构进行了高度优化。ElasticSearch为了提高效率,可以将索引下的DocumentValues全部读入内存中进行操作,大大提高了访问速度,但也消耗了大量的内存空间。总之,InvertedIndex、StoredFields、DocumentValues这些数据结构以及它们的缓存都在segment内部。当搜索发生时,Lucene会搜索所有的Segment,然后返回每个Segment的搜索结果,最后合并呈现给客户端。Lucene的一些特性使得这个过程非常重要:Segmentsareimmutable(不可变):删除?当发生删除时,Lucene所做的只是将其标记为已删除,但文件仍将位于其原始位置,不会发生更改。更新?所以对于更新来说,本质上它所做的是:先删除,再重新索引(Re-index)。无处不在的压缩:Lucene非常擅长压缩数据,基本上教科书上所有的压缩方式在Lucene中都能找到。Cacheeverything:Lucene也会缓存所有的信息,这大大提高了它的查询效率。缓存的故事ElasticSearch在索引一个文件时,会为该文件创建一个相应的缓存,并周期性(每秒)刷新数据,然后就可以搜索到这些文件了。随着时间的增加,我们会有很多Segment,如下图:所以ElasticSearch会合并这些Segment,在这个过程中,Segment最终会被删除。这就是为什么添加文件可能会使索引占用空间更小,这会导致合并,从而可能会进行更多压缩。比如两个segment会被合并:这两个segment最终会被删除,然后合并成一个新的segment,如下图所示:此时新的segment在缓存中处于冷态,但是大部分ofthesegments仍然保持不变,处于Warm状态。上述场景在LuceneIndex内部经常出现,如下图所示:在ShardElasticSearch中搜索从Shard中搜索的过程与在LuceneSegment中搜索的过程类似。与在LuceneSegment中搜索不同,分片可能分布在不同的Node上,因此在搜索和返回结果时,所有信息都将通过网络传输。需要注意的是:1次搜索2个shards=2次分别搜索shards。日志文件的处理:当我们要查找特定日期产生的日志时,按照时间戳对日志文件进行划分索引,会大大提高查找效率。当我们要删除旧数据时也很方便,删除旧索引即可。在上面的例子中,每个Index有两个Shards。扩容方式如下图所示:Shards不会进一步分裂,但shards可能会转移到不同的节点。因此,如果集群节点的压力增长到一定程度,我们可能会考虑增加新的节点,这将需要我们重新索引所有数据,这是我们不希望看到的。所以我们在规划的时候需要想清楚,如何平衡足够的节点和不足的节点之间的关系。节点分配和Shard优化:为更重要的数据索引节点分配性能更好的机器。确保每个Shard都有副本信息Replica。路由:每个节点维护一个路由表,所以当一个请求到达任何一个节点时,ElasticSearch有能力将请求转发到期望节点的Shard上做进一步的处理。一个真实的请求如下图:①Query如下图:Query有一个typefiltered和一个multi_matchquery。②聚合如下图所示:按照作者聚合,得到Top10hits的前10个作者信息。③请求分发这个请求可能分发到集群中的任意一个节点,如下图:④上帝节点如下图:此时这个节点成为当前请求的协调者(Coordinator),它决定:根据索引信息判断请求将路由到哪个核心节点。以及哪个副本可用。etc.⑤路由如下图:⑥ElasticSearch会在实际搜索前将Query转为LuceneQuery,如下图:然后在所有段进行计算,如下图:还会有一个针对Filter条件本身的缓存,如下图所示:但是Queries不会被缓存,所以如果重复执行同一个Query,应用本身就需要做缓存。所以:过滤器可以随时使用。查询仅在需要分数时使用。⑦返回搜索完成后,会沿着向下的路径逐层返回结果,如下图:作者:Richaaaard编辑:陶家龙来源:http://dwz.date/c3fC
