ES相关

本文深入探讨了Elasticsearch的核心技术,包括其架构原理、倒排索引机制、Master选举流程、脑裂问题及其解决方案、索引文档过程、更新与删除文档流程、搜索过程、读写一致性保障、动态索引策略以及性能优化方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.ES 的简单介绍
Elasticsearch 是一个基于 Lucene 的搜索引擎。它提供了具有 HTTP Web 界面和无架构 JSON 文档的分布式,多租户能力的全文搜索引擎。Lucene是一个全文搜索框架,而不是应用产品

2.倒排索引
传统的检索方式是通过文章,逐个遍历找到对应关键词的位置。
倒排索引,是通过分词策略,形成了词和文章的映射关系表,也称倒排表,这种词典 + 映射表即为倒排索引。
其中词典中存储词元,倒排表中存储该词元在哪些文中出现的位置。
有了倒排索引,就能实现 O(1) 时间复杂度的效率检索文章了,极大的提高了检索效率。
加分项:
倒排索引的底层实现是基于:FST(Finite State Transducer)数据结构。
Lucene 从 4+ 版本后开始大量使用的数据结构是 FST。FST 有两个优点:
1)空间占用小。通过对词典中单词前缀和后缀的重复利用,压缩了存储空间;
2)查询速度快。O(len(str)) 的查询时间复杂度。

3.Master选举过程(Redis选举过程)
前置条件:
1)只有是候选主节点(master:true)的节点才能成为主节点。
2)最小主节点数(min_master_nodes)的目的是防止脑裂。
Elasticsearch 的选主是 ZenDiscovery 模块负责的,主要包含 Ping(节点之间通过这个RPC来发现彼此)和 Unicast(单播模块包含一个主机列表以控制哪些节点需要 ping 通)这两部分;
获取主节点的核心入口为 findMaster,选择主节点成功返回对应 Master,否则返回 null。
选举流程大致描述如下:
第一步:确认候选主节点数达标,elasticsearch.yml 设置的值 discovery.zen.minimum_master_nodes;
第二步:对所有候选主节点根据nodeId字典排序,每次选举每个节点都把自己所知道节点排一次序,然后选出第一个(第0位)节点,暂且认为它是master节点。
第三步:如果对某个节点的投票数达到一定的值(候选主节点数n/2+1)并且该节点自己也选举自己,那这个节点就是master。否则重新选举一直到满足上述条件。
补充:
o这里的 id 为 string 类型。
omaster 节点的职责主要包括集群、节点和索引的管理,不负责文档级别的管理;data 节点可以关闭 http 功能。

4.ES的脑列问题
“脑裂”问题可能的成因
1.网络问题:集群间的网络延迟导致一些节点访问不到master,认为master挂掉了从而选举出新的master,并对master上的分片和副本标红,分配新的主分片
2.节点负载:主节点的角色既为master又为data,访问量较大时可能会导致ES停止响应造成大面积延迟,此时其他节点得不到主节点的响应认为主节点挂掉了,会重新选取主节点。
3.内存回收:data节点上的ES进程占用的内存较大,引发JVM的大规模内存回收,造成ES进程失去响应。
 
脑裂问题解决方案:
1.减少误判:discovery.zen.ping_timeout节点状态的响应时间,默认为3s,可以适当调大,如果master在该响应时间的范围内没有做出响应应答,判断该节点已经挂掉了。调大参数(如6s,discovery.zen.ping_timeout:6),可适当减少误判。
2.选举触发 discovery.zen.minimum_master_nodes:1
该参数是用于控制选举行为发生的最小集群主节点数量。
当备选主节点的个数大于等于该参数的值,且备选主节点中有该参数个节点认为主节点挂了,进行选举。官方建议为(n/2)+1,n为主节点个数(即有资格成为主节点的节点个数)
增大该参数,当该值为2时,我们可以设置master的数量为3,这样,挂掉一台,其他两台都认为主节点挂掉了,才进行主节点选举。
如果ES集群为2,那么(n/2)+1 = 2,此时就无法选举master节点,所以一般来说ES集群最小节点数设置为3。
如果节点选举时出现了对半的情况,这种情况没有节点达到了(n/2)+1 的值,所以会重新进行选举。
3.角色分离:即master节点与data节点分离,限制角色
主节点配置为:
node.master: true node.data: false
从节点配置为:
node.master: false node.data: true

5.ES索引文档过程
这里的索引文档应该理解为文档写入 ES,创建索引的过程。
文档写入包含:单文档写入和批量 bulk 写入,这里只解释一下:单文档写入流程。

记住官方文档中的这个图。

第一步:客户写集群某节点写入数据,发送请求。(如果没有指定路由/协调节点,
请求的节点扮演路由节点的角色。)

第二步:节点 1 接受到请求后,使用文档_id 来确定文档属于分片 0。请求会被转
到另外的节点,假定节点 3。因此分片 0 的主分片分配到节点 3 上。

第三步:节点 3 在主分片上执行写操作,如果成功,则将请求并行转发到节点 1
和节点 2 的副本分片上,等待结果返回。所有的副本分片都报告成功,节点 3 将
向协调节点(节点 1)报告成功,节点 1 向请求客户端报告写入成功。

如果面试官再问:第二步中的文档获取分片的过程?
回答:借助路由算法获取,路由算法就是根据路由和文档 id 计算目标的分片 id 的
过程。

1shard = hash(_routing) % (num_of_primary_shards)

6.ES更新和删除文档过程
删除和更新也都是写操作,但是 Elasticsearch 中的文档是不可变的,因此不能被删除或者改动以展示其变更。
磁盘上的每个段都有一个相应的 .del 文件。当删除请求发送后,文档并没有真的被删除,而是在 .del 文件中被标记为删除。该文档依然能匹配查询,但是会在结果中被过滤掉。当段合并时,在 .del 文件中被标记为删除的文档将不会被写入新段。
在新的文档被创建时,Elasticsearch 会为该文档指定一个版本号,当执行更新时,旧版本的文档在 .del 文件中被标记为删除,新版本的文档被索引到一个新段。旧版本的文档依然能匹配查询,但是会在结果中被过滤掉。

7.ES搜索过程
客户端发送请求到任意一个 node,成为 coordinate node
搜索被执行成一个两阶段过程,即 Query Then Fetch;
Query阶段:
查询会广播到索引中每一个分片拷贝(主分片或者副本分片)。每个分片在本地执行搜索并构建一个匹配文档的大小为 from + size 的优先队列。PS:在搜索的时候是会查询Filesystem Cache的,但是有部分数据还在Memory Buffer,所以搜索是近实时的。
每个分片返回各自优先队列中 所有文档的 ID 和排序值 给协调节点,它合并这些值到自己的优先队列中来产生一个全局排序后的结果列表。
Fetch阶段:
协调节点辨别出哪些文档需要被取回并向相关的分片提交多个 GET 请求。每个分片加载并 丰富 文档,如果有需要的话,接着返回文档给协调节点。一旦所有的文档都被取回了,协调节点返回结果给客户端。
8.ES的读写一致
数据一致性保障
持久性:通过Replica和TransLog两种机制来共同保障。一致性:数据写入成功后,需完成refresh操作之后才可读,由于无法保证Primary和Replica可同时refresh,所以会出现查询不稳定的情况,这里只能实现最终一致性。
原子性:Add和Delete直接调用Lucene的接口,进行原子操作。update操作通过Delete-Then-Add完成,在Delete操作之前会加Refresh Lock,禁止Refresh操作,等Add操作完成后释放Refresh Lock后才能被Refresh,这样就保证了Delete-Then-Add的原子性。
隔离性:采用Version和局部锁来保证更新的是特定版本的数据。要保证数据写入到ElasticSerach是安全的,高可靠的,需要如下的配置:
设置wait_for_active_shards参数大于等于2。设置TransLog的Flush策略为每个请求都要Flush。
9.动态索引
动态索引即是根据索引模板创建索引。一般日志系统都是用日期模板创建索引。也是对查询调优的一种手段,不然某个索引数据量过大时,查询就会越久。
3.1 动态索引层面
基于模板+时间+rollover api 滚动创建索引,举例:设计阶段定义:blog 索
引的模板格式为:blog_index_时间戳的形式,每天递增数据。
这样做的好处:不至于数据量激增导致单个索引数据量非常大,接近于上线 2 的
32 次幂-1,索引存储达到了 TB+甚至更大。
一旦单个索引很大,存储等各种风险也随之而来,所以要提前考虑+及早避免。

10.ES的优化
1.1、设计阶段调优
1、根据业务增量需求,采取基于日期模板创建索引,通过 roll over API 滚动索
引;
2、使用别名进行索引管理;
3、每天凌晨定时对索引做 force_merge 操作,以释放空间;
4、采取冷热分离机制,热数据存储到 SSD,提高检索效率;冷数据定期进行 shrink
操作,以缩减存储;
5、采取 curator 进行索引的生命周期管理;
6、仅针对需要分词的字段,合理的设置分词器;
7、Mapping 阶段充分结合各个字段的属性,是否需要检索、是否需要存储等。………

1.2、写入调优
1、写入前副本数设置为 0;
2、写入前关闭 refresh_interval 设置为-1,禁用刷新机制;
3、写入过程中:采取 bulk 批量写入;
4、写入后恢复副本数和刷新间隔;
5、尽量使用自动生成的 id。

1.3、查询调优
1、禁用 wildcard;
2、禁用批量 terms(成百上千的场景);
3、充分利用倒排索引机制,能 keyword 类型尽量 keyword;
4、数据量大时候,可以先基于时间敲定索引再检索;
5、设置合理的路由机制。

1.4、其他调优
部署调优,业务调优等。
上面的提及一部分,面试者就基本对你之前的实践或者运维经验有所评估了。

11.ES的分页
from + size 浅分页
"浅"分页可以理解为简单意义上的分页。它的原理很简单,就是查询前20条数据,然后截断前10条,只返回10-20的数据。这样其实白白浪费了前10条的查询。越往后的分页,执行的效率越低。总体上会随着from的增加,消耗时间也会增加。而且数据量越大,就越明显!
scroll 深分页
from+size查询在10000-50000条数据(1000到5000页)以内的时候还是可以的,但是如果数据过多的话,就会出现深分页问题。
为了解决上面的问题,elasticsearch提出了一个scroll滚动的方式。
scroll 类似于sql中的cursor,使用scroll,每次只能获取一页的内容,然后会返回一个scroll_id。根据返回的这个scroll_id可以不断地获取下一页的内容,所以scroll并不适用于有跳页的情景。

search_after 深分页
scroll 的方式,官方的建议不用于实时的请求(一般用于数据导出),因为每一个 scroll_id 不仅会占用大量的资源,而且会生成历史快照,对于数据的变更不会反映到快照上。
search_after 分页的方式是根据上一页的最后一条数据来确定下一页的位置,同时在分页请求的过程中,如果有索引数据的增删改查,这些变更也会实时的反映到游标上。但是需要注意,因为每一页的数据依赖于上一页最后一条数据,所以无法跳页请求。
为了找到每一页最后一条数据,每个文档必须有一个全局唯一值,官方推荐使用 _uid 作为全局唯一值,其实使用业务层的 id 也可以。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值