查看原文
其他

【315期】面试官问:在大数据量情况下,如何优化 ElasticSearch 查询?

Java精选 2022-08-09

点击上方“Java精选”,选择“设为星标”

别问别人为什么,多问自己凭什么!

下方有惊喜,留言必回,有问必答!

每一天进步一点点,是成功的开始...

在数据规模很大(比如万恶的trace日志)的时候我们有时候会发现第一次访问查询特别慢,可能有几秒钟的样子,然后后面的访问就很快了,这是为啥?

一、filesystem的影响

filesystem类似于我们在mysql上建立一层redis缓存;
es的搜索引擎严重依赖于底层的filesystem cache,如果给filesystem cache更多的内存,尽量让内存可以容纳所有的indx segment file索引数据文件,那么你搜索的时候就基本都是走内存的,性能会非常高。
两者差距非常大,走磁盘和走systenfile cache的读取的性能差距可以说是秒级和毫秒级的差距了;
举个反例:

有个同学es节点有3台机器,每台机器,看起来内存很多,64G,总内存,64 * 3 = 192g
但是每台机器给es jvm heap是32G,那么剩下来留给filesystem cache的就是每台机器才32g,总共集群里给filesystem cache的就是32 * 3 = 96g内存;
结果es里写了1t的数据,那么一个结点就是333G数据,只有十分之一的数据可以进走内存就返回,剩下的全都走磁盘,这样就导致我们数据可能十秒钟才写到cache再回去;

要让es性能要好,最佳的情况下,就是我们的机器的内存,至少可以容纳你的数据量的一半
二、生产es的建议
1. 尽量少存数据(存储搜索条件即可),让内存可以存储更多值
最佳的情况下,是仅仅在es中就存少量的数据,存储要用来搜索的那些索引,内存留给filesystem cache的,如果就100G,那么你就控制数据量在100gb以内,相当于是,你的数据几乎全部走内存来搜索,性能非常之高,一般可以在1秒以内

比如说现在有一行数据
id name age …30个字段
但是你现在搜索,只需要根据id name age三个字段来搜索
如果你傻乎乎的往es里写入一行数据所有的字段,就会导致说70%的数据是不用来搜索的,结果硬是占据了es机器上的filesystem cache的空间,单挑数据的数据量越大,就会导致filesystem cahce能缓存的数据就越少
仅仅只是写入es中要用来检索

的少数几个字段就可以了,比如说,就写入es id name age三个字段就可以了,然后你可以把其他的字段数据存在mysql里面,我们一般是建议用es + hbase的一个架构。
hbase的特点是适用于海量数据的在线存储,就是对hbase可以写入海量数据,不要做复杂的搜索,就是做很简单的一些根据id或者范围进行查询的这么一个操作就可以了
2. 数据预热
如果确实内存不足,但是我们又存储了比较多的数据,比如只有30g给systemfile cache,但是存储了60g数据情况,这种情况可以做数据预热;
我们可以将一些高频访问的热点数据(比如微博知乎的热榜榜单数据,电商的热门商品(旗舰版手机,榜单商品信息)等等)提前预热,定期访问刷到我们es里;(比如定期访问一下当季苹果旗舰手机关键词,比如现在的iphone12)
对于那些你觉得比较热的,经常会有人访问的数据,最好做一个专门的缓存预热子系统,就是对热数据,每隔一段时间,提前访问一下,让数据进入filesystem cache里面去。这样下次别人访问的时候,一定性能会好一些。
3. 冷热分离
我们可以将冷数据写入一个索引中,然后热数据写入另外一个索引中,这样可以确保热数据在被预热之后,尽量都让他们留在filesystem os cache里,别让冷数据给冲刷掉。

假设我们有6台机器,2个索引,一个放冷数据,一个放热数据,每个索引3个shard
3台机器放热数据index;另外3台机器放冷数据index
然后这样的话,我们大量的时候是在访问热数据index,热数据可能就占总数据量的10%,此时数据量很少,几乎全都保留在filesystem cache里面了,就可以确保热数据的访问性能是很高的。
但是对于冷数据而言,是在别的index里的,跟热数据index都不再相同的机器上,大家互相之间都没什么联系了。如果有人访问冷数据,可能大量数据是在磁盘上的,此时性能差点就差点吧,就10%的人去访问冷数据;90%的人在访问热数据。

4. 尽量不走多索引的关联查询
尽量做到设计document的时候就把需要数据结构都做好,这样搜索的数据写入的时候就完成。对于一些太复杂的操作,比如join,nested,parent-child搜索都要尽量避免,性能都很差的。
5. 分页性能优化
es的分页是较坑的,为啥呢?举个例子吧,假如你每页是10条数据,你现在要查询第100页,实际上是会把每个shard上存储的前1000条数据都查到一个协调节点上,如果你有个5个shard,那么就有5000条数据,接着协调节点对这5000条数据进行一些合并、处理,再获取到最终第100页的10条数据。
因为他是分布式的,你要查第100页的10条数据,你是不可能说从5个shard,每个shard就查2条数据?最后到协调节点合并成10条数据?这样肯定不行,因为我们从单个结点上拿的数据几乎不可能正好是所需的数据。我们必须得从每个shard都查1000条数据过来,然后根据你的需求进行排序、筛选等等操作,最后再次分页,拿到里面第100页的数据。
你翻页的时候,翻的越深,每个shard返回的数据就越多,而且协调节点处理的时间越长。非常坑爹。所以用es做分页的时候,你会发现越翻到后面,就越是慢。
我们之前也是遇到过这个问题,用es作分页,前几页就几十毫秒,翻到10页之后,几十页的时候,基本上就要5~10秒才能查出来一页数据了
优化建议
1)不允许深度分页/默认深度分页性能很惨
你系统不允许他翻那么深的页,或者产品同意翻的越深,性能就越差
2)类似于app里的推荐商品不断下拉出来一页一页的
如果是类似于微博中,下拉刷微博,刷出来一页一页的,可以用scroll api
scroll api1

Elasticsearch Scroll API详解
https://blog.csdn.net/meifannao789456/article/details/89400468

scroll api2

[ElasticSearch]Java API 之 滚动搜索(Scroll API)
https://blog.csdn.net/u014589856/article/details/78775233

scroll会一次性给你生成所有数据的一个快照,然后每次翻页就是通过游标移动,获取下一页下一页这样子,性能会比上面说的那种分页性能也高很多很多
scroll的原理实际上是保留一个数据快照,然后在一定时间内,你如果不断的滑动往后翻页的时候,类似于你现在在浏览微博,不断往下刷新翻页。那么就用scroll不断通过游标获取下一页数据,这个性能是很高的,比es实际翻页要好的多的多。
缺点:
这个适合于那种类似微博下拉翻页的,不能随意跳到任何一页的场景。
因为scroll api是只能一页一页往后翻的,是不能说,先进入第10页,然后去120页,回到58页,不能随意乱跳页。所以现在很多产品,都是不允许你随意翻页的,app,也有一些网站,做的就是你只能往下拉,一页一页的翻,无论翻多少页,性能基本上都是毫秒级的
同时这个scroll是要保留一段时间内的数据快照的,需要确保用户不会持续不断翻页翻几个小时。

作者:名字是乱打的

https://www.jianshu.com/p/8a61816dbea5

公众号“Java精选”所发表内容注明来源的,版权归原出处所有(无法查证版权的或者未注明出处的均来自网络,系转载,转载的目的在于传递更多信息,版权属于原作者。如有侵权,请联系,笔者会第一时间删除处理!

------ THE END ------

精品资料,超赞福利!

☆ 主流Java进阶技术(学习资料分享)


3000+ 道各类技术面试题在线刷,最新、最全 Java 面试题!

期往精选  点击标题可跳转

【306期】微服务 Spring Cloud 架构实现分布式日志采集方案

【307期】从实现原理来讲,Nacos 为什么这么强?

【308期】Java 实现 10 万,并发去重,优雅地处理重复请求!

【309期】Spring Bean 容器启动耗时统计分析

【310期】Spring Cloud 分布式 WebSocket 集群解决方案

【311期】4种方法,实现多线程按照指定顺序执行

【312期】天坑!常见的 update 语句很容易造成 Bug 问题

【313期】SpringBoot 无侵入式,实现 API 返回统一 JSON 格式

【314期】Spring Cloud OpenFeign 接口反序列化失效,该如何解决?

 技术交流群!

最近有很多人问,有没有读者交流群!想知道如何加入?方式很简单,兴趣相投的朋友,只需要点击下方卡片,回复“加群”,即可无套路入交流群!

文章有帮助的话,在看,转发吧!

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存