😊你好,我是小航,一个正在变秃、变强的文艺倾年。
🔔本专栏《八股消消乐》旨在记录个人所背的八股文,包括Java/Go开发、Vue开发、系统架构、大模型开发、具身智能、机器学习、深度学习、力扣算法
等相关知识点,期待与你一同探索、学习、进步,一起卷起来叭!
目录
- 题目
- 答案
- 倒排索引
- 简历优化
- 优化分页查询
- 增大刷新间隔
- 批量提交
- 冷热分离
- 优化垃圾回收
- 优化swap
- 文件描述符
题目
💬技术栈:Elasticsearch、JVM、OS
🔍简历内容:熟悉Elasticsearch倒排索引机制,了解Elasticsearch常用优化手段。
通过Search After优化分页查询,利用批量提交解决了消息积压问题,对于公司日志Logstash全量同步问题,通过引入降级机制来应对业务繁忙日志同步缓慢问题;
通过优化垃圾回收算法为G1,解决了Elasicsearch触发垃圾回收的时候,停顿时间较长(一百多毫秒),最终停顿时间控制在 15ms 以内;
为响应公司降本增效,借助了索引生命周期特性,实现了冷热数据分离存储;
🚩面试问:在冷热分离中,一般冷数据我们的都是用机械硬盘,而热数据就是用固态硬盘,你知道这是为什么吗?
💡建议暂停思考10s,你有答案了嘛?如果你有不同题解,欢迎评论区留言、打卡。
答案
倒排索引
Elasticsearch 依赖于 Lucene 来维护索引,可以直接从关键字出发,找到“Elasticsearch”关键字对应的文档。
- 每当写入一个新的文档的时候,根据文档的每一个字段,Elasticsearch 会使用分词器,把每个字段的值切割成一个个关键词,每一个关键词也叫做 Term。
切割之后,Elasticsearch 会统计每一个关键词出现的频率,构建一个关键词到文档 ID、出现频率、位置的映射
,这个也叫做 posting list。
- 每个字段其实是分散统计的。
- Elasticsearch 记录了两个位置信息,
一个位置是指它是第几个词,另外一个偏移量是指整个关键词的起始位置
。
问题:存在 Elasticsearch 里的文档数不胜数,那么就意味着一个字段会有非常多的关键词。比如说图片里的 desc 字段,如果你的系统有一亿用户,每个人写的内容都不一样,关键词就会非常多。
Elasticsearch 有 Term Dictionary。目标是尽可能地把全部关键词组成的索引整个装进内存里
。FST(Finite State Transducers)核心思想:将公共前缀、后缀也一并压缩了
。
现在有两个关键词 cat 和 ct:
和前缀树的区别:
Elasticsearch 会在 Block 内部有很多的关键词的时候,进一步切割成 Floor Block。每个 Floor Block 使用第一个关键词的首字母来加快查找。在 Block 或者 Floor Block 内部,都是通过遍历来查找对应的关键词的
- 根据 FST 找到 Block。
- 在 Block 里面遍历找到关键词。如果 Block 进一步细分为 Floor Block,就先根据前缀找到 Floor Block,然后再去遍历 Floor Block。
找到了关键词,也就找到了这个关键词对应的 posting list
,那么就可以根据文档 ID 来找到具体的文档了。
简历优化
前置准备:
- 你们公司 Elasticsearch 是如何部署的,有几个节点,每个节点上面的内存有多大,这些内存是怎么分配的?
- 你们公司 Elasticsearch 上 JVM 的配置是什么,垃圾回收用的是哪个,垃圾回收停顿的时间有多长?
- 你们公司 Elasticsearch 的哪些配置和默认值不一样,为什么修改?
- 你们公司 Elasticsearch 的性能怎么样,能撑住多大的读写流量?
优化分页查询
- Scroll 和 Scroll Scan:这种方式
适合一次性查询大量的数据
,比如说导出数据之类的场景。这种用法更加接近你在别的语言或者中间件里面接触到的游标的概念。 - Search After:也就是
翻页,你在查询的时候需要在当次查询里面带上上一次查询中返回的 search_after 字段
。【推荐】
·
对于Search After:
Search After 有点类似于分库分表中使用的禁用跳页查询
的方案(),也就是不支持随机翻页
。每次查询都带上上一次查询的 search_after 字段。
它的优点就是查询的数据量不再和偏移量有关,只和每一页的大小,以及命中的分片数量有关
。
增大刷新间隔
场景:数据最开始是写入到 Buffer 中,然后经过 refresh 才被写入到 Page Cache。频繁地把数据刷新到 Page Cache里,性能造成了损耗
。
把 index.refresh_interval 调大到了 30。调整之后,性能大概提升了 20%
。
批量提交
化单个为批量:
- 场景:把数据库里的数据同步到 Elasticsearch。公司的很多业务都不是直接同步数据到 Elasticsearch,而是要经过一个 Kafka,然后由 Kafka 的消费者把数据写入到 ElasticSearch。
- 优化前:Kafka 的消费者是每次消费一条消息,就写一条数据到 Elasticsearch。
- 优化后:由于
这个业务对数据一致性的要求不是很高
,把实时同步修改成了异步同步
。现在改成了一次拉取 100 条消息(这个应该是可配置的),做成一个批次,提交给 Elasticsearch,就解决了消息积压的问题。这个异步任务我设置的是每秒钟同步一次,和原来每一次写请求都要操作 Elasticsearch 比起来,压力小多了
。同时业务方的响应时间也下降了 50%。
调整批次:
- 场景:日志同步。
- 优化前:公司的日志都是要
利用 Logstash 直接同步到 Elasticsearch 的
。那么在业务繁忙的时候,日志就有可能同步得很慢,或者 Elasticsearch 压力很大。 - 优化后:
- 把日志同步到 Elasticsearch 的批次调大了一些,显著降低了 Elasticsearch 的负载。
- 引入降级机制:正常我们公司的日志是
全量同步
的,但是如果发现 Elasticsearch 有问题,就会触发降级,触发降级的时候就会先丢弃 INFO 级别的日志
。运行一段时间之后,如果 Elasticsearch 还是没能恢复正常,就把WARN 级别的也丢弃
。
优化不必要字段:
场景:一个历史系统需要把数据同步到 Elasticsearch 上支持搜索。把一些根本不会搜索到的字段也存储到了 Elasticsearch
,还使用了动态的 mapping,这可能无意间索引了很多新字段
。
优化前:最开始的时候数据量并不是很大,所以是直接把全量数据同步到了 Elasticsearch 上的。后面业务起来之后,就发现我这个业务的数据占据了大量的内存和磁盘空间。
优化后:其实不用全部同步过去的,因为要被搜索的字段只是其中的一小部分,而另外一些字段同步过去只是白白加重了 Elasticsearch 的负担
。修改了同步的过程,那一部分数据就直接传入 null 了。查询过程就相当于在 Elasticsearch 上根据各种输入查到业务的主键,如果还需要 Elasticsearch 中没有的字段,就回数据库再次查询。受影响的请求不足 10%,所以这个结果还是可以接受的
。
冷热分离
方案基本思路:
- 冷数据:使用运行在廉价服务器上的 Elasticsearch 来存储;
- 热数据:使用运行在昂贵的高性能服务器上的 Elasticsearch。
场景:冷热数据分离存储
优化前:数据最开始都是存在统一规格的 Elasticsearch 上,然后这两年经济不太好,就想着降本增效
。
优化后:整个 Elasticsearch 的节点分成冷热两类,数据最开始都是写入到热节点上。等过一段时间之后,数据已经不热了,就迁移到冷节点上。这部分我们是借助了新出来的索引生命周期特性来实现的
。比如说我们的日志,就是三天内的数据都在热节点上,三天之后就是迁移到了冷节点上
。这个过程都是自动的,不需要人工介入。
优化垃圾回收
正常生产环境上的 Elasticsearch 大概率都优化过了。
场景:Elasticsearch 需要一个很大的堆,那么 CMS 是肯定撑不住的,停顿时间会非常长。
解决方案:考虑把垃圾回收算法换成 G1
,或者更加激进的 ZGC。现在用 G1 比较多。
优化前:长期以来我们使用 Elasticsearch 有一个很大的问题,就是触发垃圾回收的时候,停顿时间比较长,会有一百多毫秒
。
原因:这主要是因为我们的 Elasicsearch 用的都还是非常古老的 CMS,而 CMS 在超过 8G 的堆上面,表现就比较差
。
优化后:将 Elasticsearch 换成了 G1,换了之后效果是非常不错,现在停顿时间都可以控制在 15ms 以内
。不过我也在调研 ZGC,ZGC 在特大堆上的表现比 G1 还要好。不过目前这方面业界的实践不多,所以我也没有进一步优化
。
优化swap
正常生产环境上的 Elasticsearch 大概率都优化过了。
场景:Elasticsearch 是一个内存依赖非常严重的中间件
,在触发了 swap 的时候,性能下降得很快。
解决方案:
在操作系统层面上直接禁用了 swap
,或者把 vm.swappness 设置成一个非常小的值
。- 在 Elasticsearch里
把bootstrap.memory_lock 设置成 true
。
所有类似于 Elasticsearch 的中间件都可以采用这种优化手段,比如说 Kafka。
文件描述符
正常生产环境上的 Elasticsearch 大概率都优化过了。
Elasticsearch 需要非常多的文件描述符,所以正常来说都需要把文件描述符的数量调大,比如说调到 65536,甚至更多。
场景:在使用 Elasticsearch 的时候,还遇到过文件描述符耗尽
的问题。这是因为我们用的是一个非常大的 Elasticsearch,很多业务共用,导致 Elasticsearch 打开的文件描述符非常多
。
解决方案:
- 调大了最大文件描述符的数量。
- 把业务逐步迁移到不同的 Elasticsearch 上。毕竟这一次故障,直接导致了我们好几个核心业务都出问题了,足以说明还是要考虑隔离的。
往期精彩专栏内容,欢迎订阅:
🔗【八股消消乐】20250620:[Elasticsearch优化—检索Labubu]https://blog.csdn.net/m0_51517236/article/details/148790012)
🔗【八股消消乐】20250619:构建微服务架构体系—保证服务高可用
🔗【八股消消乐】20250615:构建微服务架构体系—链路超时控制
🔗【八股消消乐】20250614:构建微服务架构体系—实现制作库与线上库分离
🔗【八股消消乐】20250612:构建微服务架构体系—限流算法优化
🔗【八股消消乐】20250611:构建微服务架构体系—降级策略全总结
🔗【八股消消乐】20250610:构建微服务架构体系—熔断恢复抖动优化
🔗【八股消消乐】20250609:构建微服务架构体系—负载均衡算法如何优化
🔗【八股消消乐】20250608:构建微服务架构体系—服务注册与发现
🔗【八股消消乐】20250607:MySQL存储引擎InnoDB知识点汇总
🔗【八股消消乐】20250606:MySQL参数优化大汇总
🔗【八股消消乐】20250605:端午节产生的消费数据,如何分表分库?
🔗【八股消消乐】20250604:如何解决SQL线上死锁事故
🔗【八股消消乐】20250603:索引失效与优化方法总结
🔗【八股消消乐】20250512:慢SQL优化手段总结
🔗【八股消消乐】20250511:项目中如何排查内存持续上升问题
🔗【八股消消乐】20250510:项目中如何优化JVM内存分配?
🔗【八股消消乐】20250509:你在项目中如何优化垃圾回收机制?
🔗【八股消消乐】20250508:Java编译优化技术在项目中的应用
🔗【八股消消乐】20250507:你了解JVM内存模型吗?
🔗【八股消消乐】20250506:你是如何设置线程池大小?
🔗【八股消消乐】20250430:十分钟带背Duubo中大厂经典面试题
🔗【八股消消乐】20250429:你是如何在项目场景中选取最优并发容器?
🔗【八股消消乐】20250428:你是项目中如何优化多线程上下文切换?
🔗【八股消消乐】20250427:发送请求有遇到服务不可用吗?如何解决?
📌 [ 笔者 ] 文艺倾年
📃 [ 更新 ] 2025.6.22
❌ [ 勘误 ] /* 暂无 */
📜 [ 声明 ] 由于作者水平有限,本文有错误和不准确之处在所难免,本人也很想知道这些错误,恳望读者批评指正!