永州网,内容丰富有趣,生活中的好帮手!
永州网 > 知识 > 正文

JAVA 经典面试题:ES如何做到亿级数据查询毫秒级返回?

时间:2024-03-15 18:36:01

相关推荐

本文将深入探讨在Java环境下,如何利用Elasticsearch(ES)实现对亿级数据的快速查询和毫秒级返回。我们将探讨ES的索引优化、分片设置、查询优化等方面的技术,以及JAVA代码中如何与ES进行高效交互。通过本文,读者将了解如何利用ES在大数据环境下实现高性能的数据查询与返回,为面试和工作中常见的技术挑战提供解决思路。

面试题

#elasticsearch#lasticsearch在数十亿级别以上的大量数据下如何提高查询效率,做到毫秒级数据查询呢?

面试官心理分析

在 JAVA 高级面试中这个问题肯定是要被问及的,为什么呢,因为可以知道你有没有实际操作过elasticsearch。

其实elasticsearch的性能并没有大家想象中的那么好,当数据量非常大的时候性能问题就会显现出来了。比如当有几十亿的数据的时候,跑一个查询可能需要10来秒中左右,这时候你就懵了,什么原因啊。但是你可能又发现一个现象:第一次搜索的时候很慢,后面再次搜索的时候就很快了,基本就是在100毫秒左右,这是啥玩意儿?

所以我会说这个问题,面试官实际就是想看你有没有实际操作过elasticsearch,如果你只是自己学习学习,玩玩 Demo 的话,那你肯定就是一脸疑问了。

不过,你很幸运看到这篇文章,接下来就带着心中的疑问往下看,去寻找答案吧......

面试题剖析

在elasticsearch中要做性能优化必须根据场景来进行优化,什么意思?就是不可能调整一个参数或者换种语法就可以实用于所有环境,这或许在部分场景中可以,但是绝对不是在所有的场景下都实用。

下面进入正题......

性能优化的杀手锏——filesystem cache

elasticsearch的数据实际上是保存导磁盘文件中的,也就是说我们向elasticsearch中写入数据,实际上都是写到磁盘文件里去了的。

当我们查询的时候,操作系统会将磁盘文件的数据自动缓存导 filesystem cache 里面去,如下图。

elasticsearch搜索引擎是严重依赖于底层操作系统的 filesystem cache 的,加入 filesystem cache 足够多的内存,尽可能让内存可以容纳elasticsearch中所有的 idx segment file 索引文件数据,这样你在搜索的时候基本上都是走的 filesystem cache 的内存,这样性能肯定非常高的。

那么走磁盘和走内存两者的性能到底有多大的差异呢?

这个问题我们也在实际环境中做过测试:如果走磁盘,搜索性能一般都是秒级别的,从1秒、2秒到几十秒不等。但是如果走内存那这个性能就要高出一个数量级,基本都是毫秒级别的,从几毫秒到几百毫秒不等。

一个真实的案例:

某个公司 elasticsearch 节点有 3 台机器,每台机器看起来内存很多,64G,总内存就是 64 * 3 = 192G。每台机器给 es jvm heap 是 32G,那么剩下来留给 filesystem cache的就是每台机器才 32G,总共集群里给 filesystem cache 的就是 32 * 3 = 96G 内存。而此时,整个磁盘上索引数据文件,在 3 台机器上一共占用了 1T 的磁盘容量,es 数据量是 1T,那么每台机器的数据量是 300G。这样性能好吗?filesystem cache 的内存才 100G,十分之一的数据可以放内存,其他的都在磁盘,然后你执行搜索操作,大部分操作都是走磁盘,性能肯定差。

上面的案列总结起来就是:如果你想让你的elasticsearch搜索性能更好,就尽可能的让你的机器内存可以容纳更多的索引数据。根据实践,最佳的情况下至少可以容纳你的总数据的一半。

我们自己生产环境实践经验:最佳的情况下,是仅仅在 elasticsearch 中存你要用来搜索的那些索引数据,这样减少了elasticsearch的数据索引的大小,就能尽可能的让搜索数据都保存到filesystem cache 内存中。如果内存留给 filesystem cache 的是 100G,那么你就将索引数据控制在 100G 以内,这样的话,你的数据几乎全部走内存来搜索,性能非常之高,一般可以在 1 秒以内。

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

其实,仅仅写入 elasticsearch 中要用来检索的少数几个字段就可以了,比如说就写入 es id,name,age 三个字段,然后你可以把其他的字段数据存在 mysql/hbase 里,我们一般是建议用 elasticsearch + hbase 这么一个架构。

hbase 的特点是适用于海量数据的在线存储,就是对 hbase 可以写入海量数据,但是切记不要做复杂的搜索,做一些根据 id 或者范围进行查询的简单操作就可以了。从 elasticsearch 中根据 name 和 age 去搜索,拿到的结果可能就 20 个 doc id,然后根据 doc id 到 hbase 里去查询每个 doc id 对应的完整的数据,给查出来,再返回给前端。

写入 elasticsearch 的数据最好小于等于或者是略微大于 es 的 filesystem cache 的内存容量。然后你从 elasticsearch 检索可能就花费 20ms,然后再根据 es 返回的 id 去 hbase 里查询,查 20 条数据,可能也就耗费个 30ms。

如果按照原来那么玩将1T 的数据全部放到elasticsearch中,这时有大量的数据的数据是无法保存到 filesystem cache 的内存中的,这样查询性能肯定会非常的差。

数据预热

即便就按照上面说的来做,也无法很好的解决内存和elasticsearch数据大小的问题,那还可以做些什么呢?

其实可以做数据预热。

举个例子,拿微博来说,你可以把一些大V,平时看的人很多的数据,你自己提前后台搞个系统,每隔一会儿,自己的后台系统去搜索一下热数据,刷到 filesystem cache 里去,后面用户实际上来看这个热数据的时候,他们就是直接从内存里搜索了,很快。

或者是电商,你可以将平时查看最多的一些商品,比如说 华为 mate 30,热数据提前后台搞个程序,每隔 1 分钟自己主动访问一次,刷到 filesystem cache 里去。

对于那些你觉得比较热的、经常会有人访问的数据,最好做一个专门的缓存预热子系统,就是对热数据每隔一段时间,就提前访问一下,让数据进入 filesystem cache 里面去。这样下次别人访问的时候,性能一定会好很多。

冷热分离

elasticsearch 可以做类似于 mysql 的水平拆分,就是说将大量的访问很少、频率很低的数据,单独写一个索引,然后将访问很频繁的热数据单独写一个索引。最好是将冷数据写入一个索引中,然后热数据写入另外一个索引中,这样可以确保热数据在被预热之后,尽量都让他们留在 filesystem os cache 里,别让冷数据给冲刷掉。

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

document 模型设计

对于 MySQL,我们经常有一些复杂的关联查询。那么在 elasticsearch 里该怎么做?

我想告诉你的是:在elasticsearch 中最好不要用复杂的关联查询,用了一般性能都是是非常差的。

最好的做法就在在 JAVA 程序中就将关联关系做好,然后再存入到elasticsearch中,这样查询出来的数据就是关联好的数据了,这样做性能会好很多。

温馨提示:不要考虑用 es 做一些它不好操作的事情。如果真的有那种操作,尽量在 document 模型设计的时候,写入的时候就完成。另外对于一些太复杂的操作,比如 join/nested/parent-child 搜索都要尽量避免,性能都很差的。

分页性能优化

elasticsearch 的分页是较坑的,为啥呢?

举个例子吧,假如你每页是 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 秒才能查出来一页数据了。

有什么解决方案吗?

不允许深度分页(默认深度分页性能很差)

跟产品经理说,你系统不允许翻那么深的页,默认翻的越深,性能就越差。

类似于 app 里的推荐商品不断下拉出来一页一页的

类似于微博中,下拉刷微博,刷出来一页一页的,你可以用 scroll api。

scroll 会一次性给你生成所有数据的一个快照,然后每次滑动向后翻页就是通过游标 scroll_id移动,获取下一页下一页这样子,性能会比上面说的那种分页性能要高很多很多,基本上都是毫秒级的。

这个适合于那种类似微博下拉翻页的,不能随意跳到任何一页的场景。也就是说,你不能先进入第 10 页,然后去第 120 页,然后又回到第 58 页,不能随意乱跳页。所以现在很多产品,都是不允许你随意翻页的,app,也有一些网站,做的就是你只能往下拉,一页一页的翻。

初始化时必须指定 scroll 参数,告诉 es 要保存此次搜索的上下文多长时间。你需要确保用户不会持续不断翻页翻几个小时,否则可能因为超时而失败。

除了用 scroll api,你也可以用 search_after 来做,search_after 的思想是使用前一页的结果来帮助检索下一页的数据,显然,这种方式也不允许你随意翻页,你只能一页页往后翻。初始化时,需要使用一个唯一值的字段作为 sort 字段。

想了解更多精彩内容,快来关注计算机java编程

收集不易,本文《JAVA 经典面试题:ES如何做到亿级数据查询毫秒级返回?》知识如果对你有帮助,请点赞收藏并留下你的评论。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。
显示评论内容(2)
  1. 韩筱静2024-03-15 19:00韩筱静[火星网友]43.224.12.174
    ES的查询速度真的是让人惊叹,我觉得对于大数据领域的开发者来说,掌握ES真的很重要。
    顶7踩0
  2. 南寻歌2024-03-15 18:48南寻歌[宁夏网友]202.6.189.168
    这个话题太有吸引力了,我也想了解ES如何处理亿级数据查询的问题。
    顶0踩0
相关阅读
机械专业新毕业生简历模板:积极应对挑战的大学毕业生

机械专业新毕业生简历模板:积极应对挑战的大学毕业生

通过大学期间的实习和项目经历,我熟练掌握了CAD、SolidWorks等设计软件,并具有较强的图纸绘制能力

2024-02-07

揭秘银川新媒体运营面试题答案 推荐收藏!必问的5个问题一一解析

揭秘银川新媒体运营面试题答案 推荐收藏!必问的5个问题一一解析

作为新媒体运营者,准备充分且全面地回答这些问题将有助于在面试中脱颖而出

2024-02-17

外企面试:如何应对独特的招式

外企面试:如何应对独特的招式

在面对这些怪招时,应聘者需要做好充分准备,保持镇定和灵活应对

2024-03-05

ASP.NET中如何防范SQL注入式攻击

ASP.NET中如何防范SQL注入式攻击

通过输入恶意的SQL代码,攻击者可以绕过应用程序的安全验证,访问、修改、删除数据库中的数据,甚至获取敏感信息

2024-03-12

在Spring中用select last insert id()时遇到问题

在Spring中用select last insert id()时遇到问题

这个问题通常涉及数据库操作的事务管理和自动生成的ID的获取

2023-03-05