热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

ES调优查询亿级数据毫秒级返回!怎么做到的?文件系统缓存

一道面试题的引入:如果面试的时候碰到这样一个面试题:ElasticSearch(以下简称ES)在数据量很大的情况下(数十亿级别)如何提高查询效率?这个问题说白了,就是看你有没有实际

 一道面试题的引入:

  如果面试的时候碰到这样一个面试题:ElasticSearch(以下简称ES) 在数据量很大的情况下(数十亿级别)如何提高查询效率?

  这个问题说白了,就是看你有没有实际用过 ES,因为啥?其实 ES 性能并没有你想象中那么好的。

  很多时候数据量大了,特别是有几亿条数据的时候,可能你会懵逼的发现,跑个搜索怎么一下 5~10s,坑爹了。

  第一次搜索的时候,是 5~10s,后面反而就快了,可能就几百毫秒。

  然后你就很懵,每个用户第一次访问都会比较慢,比较卡么?所以你要是没玩儿过 ES,或者就是自己玩玩儿 Demo,被问到这个问题容易懵逼,显示出你对 ES 确实玩的不怎么样?

  说实话,ES 性能优化是没有银弹的。啥意思呢?就是不要期待着随手调一个参数,就可以万能的应对所有的性能慢的场景。

  也许有的场景是你换个参数,或者调整一下语法,就可以搞定,但是绝对不是所有场景都可以这样。

  性能优化的杀手锏:Filesystem Cache

  你往 ES 里写的数据,实际上都写到磁盘文件里去了,查询的时候,操作系统会将磁盘文件里的数据自动缓存到 Filesystem Cache 里面去。

  整个过程,如下图所示:

  

技术分享图片

 

  ES 的搜索引擎严重依赖于底层的 Filesystem Cache,你如果给 Filesystem Cache 更多的内存,尽量让内存可以容纳所有的 IDX Segment File 索引数据文件,那么你搜索的时候就基本都是走内存的,性能会非常高。

  性能差距究竟可以有多大?我们之前很多的测试和压测,如果走磁盘一般肯定上秒,搜索性能绝对是秒级别的,1 秒、5 秒、10 秒。

  但如果是走 Filesystem Cache,是走纯内存的,那么一般来说性能比走磁盘要高一个数量级,基本上就是毫秒级的,从几毫秒到几百毫秒不等。

  来看一个真实的案例:某个公司 ES 节点有 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,十分之一的数据可以放内存,其他的都在磁盘,然后你执行搜索操作,大部分操作都是走磁盘,性能肯定差。

  归根结底,你要让 ES 性能好,最佳的情况下,就是你的机器的内存,至少可以容纳你的总数据量的一半。

  根据我们自己的生产环境实践经验,最佳的情况下,是仅仅在 ES 中就存少量的数据。

  也就是说,你要用来搜索的那些索引,如果内存留给 Filesystem Cache 的是 100G,那么你就将索引数据控制在 100G 以内。这样的话,你的数据几乎全部走内存来搜索,性能非常之高,一般可以在1秒以内。

  比如说你现在有一行数据:id,name,age .... 30 个字段。但是你现在搜索,只需要根据 id,name,age 三个字段来搜索。

  如果你傻乎乎往 ES 里写入一行数据所有的字段,就会导致 90% 的数据是不用来搜索的。

  但是呢,这些数据硬是占据了 ES 机器上的 Filesystem Cache 的空间,单条数据的数据量越大,就会导致 Filesystem Cahce 能缓存的数据就越少。

  其实,仅仅写入 ES 中要用来检索的少数几个字段就可以了,比如说就写入 es id,name,age 三个字段。

  然后你可以把其他的字段数据存在 MySQL/HBase 里,我们一般是建议用 ES + HBase 这么一个架构。

  HBase是列式数据库,其特点是适用于海量数据的在线存储,就是对 HBase 可以写入海量数据,但是不要做复杂的搜索,做很简单的一些根据 id 或者范围进行查询的这么一个操作就可以了。

  从 ES 中根据 name 和 age 去搜索,拿到的结果可能就 20 个 doc id,然后根据 doc id 到 HBase 里去查询每个 doc id 对应的完整的数据,给查出来,再返回给前端。

  而写入 ES 的数据最好小于等于,或者是略微大于 ES 的 Filesystem Cache 的内存容量。

  然后你从 ES 检索可能就花费 20ms,然后再根据 ES 返回的 id 去 HBase 里查询,查 20 条数据,可能也就耗费个 30ms。

  如果你像原来那么玩儿,1T 数据都放 ES,可能会每次查询都是 5~10s,而现在性能就会很高,每次查询就是 50ms。

  数据预热

  假如你就按照上述的方案去做了,ES 集群中每个机器写入的数据量还是超过了 Filesystem Cache 一倍。

  比如说你写入一台机器 60G 数据,结果 Filesystem Cache 就 30G,还是有 30G 数据留在了磁盘上。

  这种情况下,其实可以做数据预热。举个例子,拿微博来说,你可以把一些大 V,平时看的人很多的数据,提前在后台搞个系统。

  然后每隔一会儿,自己的后台系统去搜索一下热数据,刷到 Filesystem Cache 里去,后面用户实际上来看这个热数据的时候,他们就是直接从内存里搜索了,很快。

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

  总之,就是对于那些你觉得比较热的、经常会有人访问的数据,最好做一个专门的缓存预热子系统。

  然后对热数据每隔一段时间,就提前访问一下,让数据进入 Filesystem Cache 里面去。这样下次别人访问的时候,性能一定会好很多。

  冷热分离

  ES 可以做类似于 MySQL 的水平拆分,就是说将大量的访问很少、频率很低的数据,单独写一个索引,然后将访问很频繁的热数据单独写一个索引。

  最好是将冷数据写入一个索引中,然后热数据写入另外一个索引中,这样可以确保热数据在被预热之后,尽量都让他们留在 Filesystem OS Cache 里,别让冷数据给冲刷掉。

  还是来一个例子,假设你有 6 台机器,2 个索引,一个放冷数据,一个放热数据,每个索引 3 个 Shard。3 台机器放热数据 Index,另外 3 台机器放冷数据 Index。

  这样的话,你大量的时间是在访问热数据 Index,热数据可能就占总数据量的 10%,此时数据量很少,几乎全都保留在 Filesystem Cache 里面了,就可以确保热数据的访问性能是很高的。

  但是对于冷数据而言,是在别的 Index 里的,跟热数据 Index 不在相同的机器上,大家互相之间都没什么联系了。

  如果有人访问冷数据,可能大量数据是在磁盘上的,此时性能差点,就 10% 的人去访问冷数据,90% 的人在访问热数据,也无所谓了。

  ES中的关联查询

  对于 MySQL,我们经常有一些复杂的关联查询,在 ES 里该怎么玩儿?

  ES 里面的复杂的关联查询尽量别用,一旦用了性能一般都不太好。最好是先在 Java 系统里就完成关联,将关联好的数据直接写入 ES 中。搜索的时候,就不需要利用 ES 的搜索语法来完成 Join 之类的关联搜索了。

  Document 模型设计

  Document 模型设计是非常重要的,很多操作,不要在搜索的时候才想去执行各种复杂的乱七八糟的操作。

  ES 能支持的操作就那么多,不要考虑用 ES 做一些它不好操作的事情。如果真的有那种操作,尽量在 Document 模型设计的时候,写入的时候就完成。

  另外对于一些太复杂的操作,比如 join/nested/parent-child 搜索都要尽量避免,性能都很差的。

  分页性能优化

  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 秒才能查出来一页数据了。

  有什么解决方案吗?两个思路:

  一、不允许深度分页(默认深度分页性能很差)。跟产品经理说,你系统不允许翻那么深的页,默认翻的越深,性能就越差。

  二、类似于 App 里的推荐商品不断下拉出来一页一页的;类似于微博中,下拉刷微博,刷出来一页一页的,你可以用 Scroll API,关于如何使用,大家可以自行上网搜索学习一下。

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

  但是,唯一的一点就是,这个适合于那种类似微博下拉翻页的,不能随意跳到任何一页的场景。也就是说,你不能先进入第 10 页,然后去第 120 页,然后又回到第 58 页,不能随意乱跳页。

  所以现在很多产品,都是不允许你随意翻页的,你只能往下拉,一页一页的翻。

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

  除了用 Scroll API,你也可以用 search_after 来做。search_after 的思想是使用前一页的结果来帮助检索下一页的数据。

  显然,这种方式也不允许你随意翻页,你只能一页页往后翻。初始化时,需要使用一个唯一值的字段作为 Sort 字段。

转载自:https://blog.csdn.net/qianfeng_dashuju/article/details/93627336


推荐阅读
  • 在编写业务代码时,常常会遇到复杂的业务逻辑导致代码冗长混乱的情况。为了解决这个问题,可以利用中间件模式来简化代码逻辑。中间件模式可以帮助我们更好地设计架构和代码,提高代码质量。本文介绍了中间件模式的基本概念和用法。 ... [详细]
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
  • 本文内容为asp.net微信公众平台开发的目录汇总,包括数据库设计、多层架构框架搭建和入口实现、微信消息封装及反射赋值、关注事件、用户记录、回复文本消息、图文消息、服务搭建(接入)、自定义菜单等。同时提供了示例代码和相关的后台管理功能。内容涵盖了多个方面,适合综合运用。 ... [详细]
  • 本文介绍了lua语言中闭包的特性及其在模式匹配、日期处理、编译和模块化等方面的应用。lua中的闭包是严格遵循词法定界的第一类值,函数可以作为变量自由传递,也可以作为参数传递给其他函数。这些特性使得lua语言具有极大的灵活性,为程序开发带来了便利。 ... [详细]
  • 基于layUI的图片上传前预览功能的2种实现方式
    本文介绍了基于layUI的图片上传前预览功能的两种实现方式:一种是使用blob+FileReader,另一种是使用layUI自带的参数。通过选择文件后点击文件名,在页面中间弹窗内预览图片。其中,layUI自带的参数实现了图片预览功能。该功能依赖于layUI的上传模块,并使用了blob和FileReader来读取本地文件并获取图像的base64编码。点击文件名时会执行See()函数。摘要长度为169字。 ... [详细]
  • 本文介绍了在SpringBoot中集成thymeleaf前端模版的配置步骤,包括在application.properties配置文件中添加thymeleaf的配置信息,引入thymeleaf的jar包,以及创建PageController并添加index方法。 ... [详细]
  • JVM 学习总结(三)——对象存活判定算法的两种实现
    本文介绍了垃圾收集器在回收堆内存前确定对象存活的两种算法:引用计数算法和可达性分析算法。引用计数算法通过计数器判定对象是否存活,虽然简单高效,但无法解决循环引用的问题;可达性分析算法通过判断对象是否可达来确定存活对象,是主流的Java虚拟机内存管理算法。 ... [详细]
  • Java验证码——kaptcha的使用配置及样式
    本文介绍了如何使用kaptcha库来实现Java验证码的配置和样式设置,包括pom.xml的依赖配置和web.xml中servlet的配置。 ... [详细]
  • 高质量SQL书写的30条建议
    本文提供了30条关于优化SQL的建议,包括避免使用select *,使用具体字段,以及使用limit 1等。这些建议是基于实际开发经验总结出来的,旨在帮助读者优化SQL查询。 ... [详细]
  • 本文介绍了指针的概念以及在函数调用时使用指针作为参数的情况。指针存放的是变量的地址,通过指针可以修改指针所指的变量的值。然而,如果想要修改指针的指向,就需要使用指针的引用。文章还通过一个简单的示例代码解释了指针的引用的使用方法,并思考了在修改指针的指向后,取指针的输出结果。 ... [详细]
  • 在project.properties添加#Projecttarget.targetandroid-19android.library.reference.1..Sliding ... [详细]
  • 猜字母游戏
    猜字母游戏猜字母游戏——设计数据结构猜字母游戏——设计程序结构猜字母游戏——实现字母生成方法猜字母游戏——实现字母检测方法猜字母游戏——实现主方法1猜字母游戏——设计数据结构1.1 ... [详细]
  • CentOS 7部署KVM虚拟化环境之一架构介绍
    本文介绍了CentOS 7部署KVM虚拟化环境的架构,详细解释了虚拟化技术的概念和原理,包括全虚拟化和半虚拟化。同时介绍了虚拟机的概念和虚拟化软件的作用。 ... [详细]
  • 本文介绍了一种解析GRE报文长度的方法,通过分析GRE报文头中的标志位来计算报文长度。具体实现步骤包括获取GRE报文头指针、提取标志位、计算报文长度等。该方法可以帮助用户准确地获取GRE报文的长度信息。 ... [详细]
  • PDF内容编辑的两种小方法,你知道怎么操作吗?
    本文介绍了两种PDF内容编辑的方法:迅捷PDF编辑器和Adobe Acrobat DC。使用迅捷PDF编辑器,用户可以通过选择需要更改的文字内容并设置字体形式、大小和颜色来编辑PDF文件。而使用Adobe Acrobat DC,则可以通过在软件中点击编辑来编辑PDF文件。PDF文件的编辑可以帮助办公人员进行文件内容的修改和定制。 ... [详细]
author-avatar
莣Q楽
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有