ElasticSearch 的运行原理(图解)
![undefined](https://filescdn.proginn.com/2febf23edd4ed0710852022a68376c70/619e5caf029250fe01b71ccf1b129c83.webp)
帮助万千Java学习者持续成长
来源|Richaaaard
B 站搜索:楠哥教你学Java
获取更多优质视频教程
Elasticsearch 是一款功能强大的开源分布式搜索与数据分析引擎,目前国内诸多互联网大厂都在使用,包括携程、滴滴、今日头条、饿了么、360 安全、小米、vivo 等。
除了搜索之外,结合 Kibana、Logstash、Beats,Elastic Stack 还被广泛运用在大数据近实时分析领域,包括日志分析、指标监控、信息安全等多个领域。
它可以帮助你探索海量结构化、非结构化数据,按需创建可视化报表,对监控数据设置报警阈值,甚至通过使用机器学习技术,自动识别异常状况。
今天,我们先自上而下,后自底向上的介绍ElasticSearch的底层工作原理,并试图回答以下问题:
为什么我的搜索 *foo-bar* 无法匹配 foo-bar ?
为什么增加更多的文件会压缩索引(Index)?
为什么 ElasticSearch 占用很多内存?
①云上的集群,如下图:
![](https://filescdn.proginn.com/f16e476b12d8fe4d04e957444fe273ca/687ee256ad0eda8eb3a0f73b4bf6d0d5.webp)
②集群里的盒子,云里面的每个白色正方形的盒子代表一个节点——Node。
![](https://filescdn.proginn.com/d7476353733efa36ea92bc227f6681d7/2dc6da7aabf826c02781a103b98f6660.webp)
③节点之间,在一个或者多个节点直接,多个绿色小方块组合在一起形成一个 ElasticSearch 的索引。
![](https://filescdn.proginn.com/2f07afa8b4510ba626f01ec98b8bb206/57060fb31f91acefd4714977b4f848b5.webp)
④索引里的小方块,在一个索引下,分布在多个节点里的绿色小方块称为分片——Shard。
![](https://filescdn.proginn.com/03f5fcc128a15186d4e29668335c8d86/8117ad87cd0dd547262bfc0d19e388cf.webp)
⑤Shard=Lucene Index,一个 ElasticSearch 的 Shard 本质上是一个 Lucene Index。
![](https://filescdn.proginn.com/ee67c7ff265bb319ea23fe615448ab20/72e0d246821a3c64de31f3d12e02a4b2.webp)
Lucene 是一个 Full Text 搜索库(也有很多其他形式的搜索库),ElasticSearch 是建立在 Lucene 之上的。
![](https://filescdn.proginn.com/a4a84df118a4832249c185804dc0b60e/b94f46ee89b2ee5aba0de1a2a0fed55b.webp)
![](https://filescdn.proginn.com/818c2a87c25cf7d4a140d8cf0874f4e5/15295b519d8a6ff760f0eeb0bc488b9e.webp)
Segment 内部有着许多数据结构,如上图:
Inverted Index
Stored Fields
Document Values
Cache
![](https://filescdn.proginn.com/4d3322a8afd7e4019b966bf851ea2a02/cd16523fb8ca583c4f059388e3f29923.webp)
Inverted Index 主要包括两部分:
一个有序的数据字典 Dictionary(包括单词 Term 和它出现的频率)。
与单词 Term 对应的 Postings(即存在这个单词的文件)。
![](https://filescdn.proginn.com/103a170fd2c0bf4e6a2794ac73d86b35/8998c2aaa60bad90be1ddbbf24b090fc.webp)
①查询“the fury”,如下图:
![](https://filescdn.proginn.com/eb2a9175dd07b52745570c39efb61a17/64d52badb206ea9b6e1cdb20b8ad52aa.webp)
②自动补全(AutoCompletion-Prefix)
![](https://filescdn.proginn.com/17e852d8feff252bc91e1a032b481e8d/6e1d38dbb6657e5482f0596715e8728d.webp)
③昂贵的查找
![](https://filescdn.proginn.com/487fba0a99ad2602a2ccb697e5535e13/c77656c1a07cd53964d7c845e9feb043.webp)
在此种情况下,如果想要做优化,那么我们面对的问题是如何生成合适的 Term。
④问题的转化,如下图:
![](https://filescdn.proginn.com/640fac1e1aa3da13687ecb29acd8c9d7/57d2262bcd2056b755d75f03351fe411.webp)
对于以上诸如此类的问题,我们可能会有几种可行的解决方案:
* suffix→xiffus *,如果我们想以后缀作为搜索条件,可以为 Term 做反向处理。
(60.6384, 6.5017)→ u4u8gyykk,对于 GEO 位置信息,可以将它转换为 GEO Hash。
123→{1-hundreds, 12-tens, 123},对于简单的数字,可以为它生成多重形式的 Term。
⑤解决拼写错误
一个 Python 库为单词生成了一个包含错误拼写信息的树形状态机,解决拼写错误的问题。
![](https://filescdn.proginn.com/ab8c852d5cc8fd0aa79aa03d765ec852/69c52a7ef6726e9ebb9318dafde4e7bf.webp)
⑥Stored Field 字段查找
当我们想要查找包含某个特定标题内容的文件时,Inverted Index 就不能很好的解决这个问题,所以 Lucene 提供了另外一种数据结构 Stored Fields 来解决这个问题。
本质上,Stored Fields 是一个简单的键值对 key-value。默认情况下,ElasticSearch 会存储整个文件的 JSON source。
![](https://filescdn.proginn.com/c32f03fd229388cca0a35ae457da7d55/8a984f709161e18197cc5f1a328677f7.webp)
⑦Document Values 为了排序,聚合
即使这样,我们发现以上结构仍然无法解决诸如:排序、聚合、facet,因为我们可能会要读取大量不需要的信息。
所以,另一种数据结构解决了此种问题:Document Values。这种结构本质上就是一个列式的存储,它高度优化了具有相同类型的数据的存储结构。
![](https://filescdn.proginn.com/78b0be00863761ec6cf5230165d5ad93/d4786b831ddb8dda8fb41b4aa38d70e4.webp)
为了提高效率,ElasticSearch 可以将索引下某一个 Document Value 全部读取到内存中进行操作,这大大提升访问速度,但是也同时会消耗掉大量的内存空间。
总之,这些数据结构 Inverted Index、Stored Fields、Document Values 及其缓存,都在 segment 内部。
搜索时,Lucene 会搜索所有的 Segment 然后将每个 Segment 的搜索结果返回,最后合并呈现给客户。
Lucene 的一些特性使得这个过程非常重要:
Segments 是不可变的(immutable):Delete?当删除发生时,Lucene 做的只是将其标志位置为删除,但是文件还是会在它原来的地方,不会发生改变。
Update?所以对于更新来说,本质上它做的工作是:先删除,然后重新索引(Re-index)。
随处可见的压缩:Lucene 非常擅长压缩数据,基本上所有教科书上的压缩方式,都能在 Lucene 中找到。
缓存所有的所有:Lucene 也会将所有的信息做缓存,这大大提高了它的查询效率。
当 ElasticSearch 索引一个文件的时候,会为文件建立相应的缓存,并且会定期(每秒)刷新这些数据,然后这些文件就可以被搜索到。
![](https://filescdn.proginn.com/54342942126fb16193b21e808d238272/44cb5f4d91938f05a662c037c30367fe.webp)
随着时间的增加,我们会有很多 Segments,如下图:
![](https://filescdn.proginn.com/a3089afd098dcac8b4b559945bd2a35e/a22873594c11bd27376cc29b2717a732.webp)
所以 ElasticSearch 会将这些 Segment 合并,在这个过程中,Segment 会最终被删除掉。
![](https://filescdn.proginn.com/b2adfba050c92bb941d5de57a6bf3e6c/cd33cada10676b0be164568d5d2dbba4.webp)
有两个 Segment 将会 Merge:
![](https://filescdn.proginn.com/18ae57df4ffebe9b2c620b20fb94c21d/9964abfdc26c7be99dab3f17e07b6fed.webp)
这两个 Segment 最终会被删除,然后合并成一个新的 Segment,如下图:
这时这个新的 Segment 在缓存中处于 Cold 状态,但是大多数 Segment 仍然保持不变,处于 Warm 状态。
ElasticSearch 从 Shard 中搜索的过程与 Lucene Segment 中搜索的过程类似。
与在 Lucene Segment 中搜索不同的是,Shard 可能是分布在不同 Node 上的,所以在搜索与返回结果时,所有的信息都会通过网络传输。
对于日志文件的处理:当我们想搜索特定日期产生的日志时,通过根据时间戳对日志文件进行分块与索引,会极大提高搜索效率。
当我们想要删除旧的数据时也非常方便,只需删除老的索引即可。
![](https://filescdn.proginn.com/9100fde09a2b427c885c2cd95b6a8759/6e2f3fc9f8dd3aa879d4f96639279789.webp)
Shard 不会进行更进一步的拆分,但是 Shard 可能会被转移到不同节点上。
节点分配与 Shard 优化:
为更重要的数据索引节点,分配性能更好的机器。
确保每个 Shard 都有副本信息 Replica。
![](https://filescdn.proginn.com/7239112d06a5428ab8b8b7bb398f4cab/0c70c7c123dbace6a57e88f615ccacc1.webp)
路由 Routing:每个节点,每个都存留一份路由表,所以当请求到任何一个节点时,ElasticSearch 都有能力将请求转发到期望节点的 Shard 进一步处理。
![](https://filescdn.proginn.com/191e8de04705d55a2975e016d0325828/018faf7b989930633a6d98fe876cff31.webp)
①Query,如下图:
![](https://filescdn.proginn.com/65f062487dc6ab55b6a2c9dfaabbbcd8/016af52b5f677b8f6059edcdf3f6dee6.webp)
②Aggregation,如下图:
![](https://filescdn.proginn.com/0228f95d0eaeb34e02160f26275d3125/bf005e7ce871afc82eeabb68d3ad1aa8.webp)
③请求分发,这个请求可能被分发到集群里的任意一个节点,如下图:
④上帝节点,如下图:
![](https://filescdn.proginn.com/639e04ab9ffba30690e1c1e61908b6b9/9bed46d0a7f5d1a26e4a70e97194e2b2.webp)
根据索引信息,判断请求会被路由到哪个核心节点。
以及哪个副本是可用的。
等等。
⑤路由,如下图:
![](https://filescdn.proginn.com/e8a5b78d7e2915a1c2e29910143e1246/571fe8a118640144c94edf9cc00bdb3c.webp)
⑥在真实搜索之前,ElasticSearch 会将 Query 转换成 Lucene Query,如下图:
然后在所有的 Segment 中执行计算,如下图:
![](https://filescdn.proginn.com/2ac97549517ccf0262ad6d03b18a7048/9578b3c38161eac753bcf1eb0949dc1b.webp)
但 Queries 不会被缓存,所以如果相同的 Query 重复执行,应用程序自己需要做缓存。
Filters 可以在任何时候使用。
Query 只有在需要 Score 的时候才使用。
⑦返回,搜索结束之后,结果会沿着下行的路径向上逐层返回,如下图:
推荐阅读
楠哥简介
资深 Java 工程师,微信号 southwindss
《Java零基础实战》一书作者
腾讯课程官方 Java 面试官,今日头条认证大V
GitChat认证作者,B站认证UP主(楠哥教你学Java)
致力于帮助万千 Java 学习者持续成长。
有收获,就点个在看