当前位置: 首页 > 后端技术 > Java

30个ElasticSearch调优知识点,都给你整理好了!

时间:2023-04-01 23:33:42 Java

ES官方调优指南第1部分:调优索引速度第2部分:调优搜索速度第3部分:一些通用建议ES在发布时自带的默认值可以给es带来非常好的开箱即用体验。无需用户修改即可使用全文搜索、突出显示、聚合和索引文档等功能。当你更了解你想如何使用es时,你可以进行大量的优化来提高你的用例的性能。以下内容告诉您应该/不应该修改哪些配置。第1部分:调整索引速度https://www.elastic.co/guide/...使用批量请求批量请求将产生比单文档索引请求更好的性能。为了了解批量请求的最佳大小,您应该在具有单个分片的单个节点上运行基准测试。首先尝试索引100个文档,然后是200,然后是400,等等。当索引速度开始稳定时,您知道您已经达到数据批处理请求的最佳大小。在合适的情况下,宁可宁可文件太少也不要文件太多。请注意,如果集群请求太大,可能会造成内存压力,因此建议避免每个请求超过几十兆字节,即使更大的请求似乎效果更好。sender使用多个worker/threads向es发送数据。发送批量请求的单个线程不太可能最大化Elasticsearch集群的索引容量。为了使用集群的所有资源,您应该从多个线程或进程发送数据。除了更好地利用集群资源之外,这应该有助于降低每次fsync的成本。请务必注意TOOMANYREQUESTS(429)响应代码(Java客户端为EsRejectedExecutionException),这是Elasticsearch告诉您它跟不上当前索引率的方式。发生这种情况时,应再次尝试暂停索引,最好使用随机指数退避。与批量调整大小请求类似,只有测试才能确定最佳工作人员数量。这可以通过逐渐增加worker数量直到集群上的I/O或CPU饱和来测试。1.增加刷新间隔。默认的index.refresh_interval是1s,这会强制Elasticsearch每秒创建一个新的段。增加这个值(比如30秒)将允许更大的部分刷新并减少未来的合并压力。2.加载大量数据时禁用刷新和副本如果需要一次加载大量数据,应将index.refreshinterval设置为-1,将index.numberofreplicas设置为0以禁用刷新。这暂时会使您的索引处于危险之中,因为丢失任何分片都会导致数据丢失,但同时索引会更快,因为文档只被索引一次。初始加载完成后,您可以将index.refreshinterval和index.numberofreplicas设置回原来的值。3.设置参数禁止OS换出es进程。您应该通过禁止交换来确保操作系统不会换出java进程(https://www.elastic.co/guide/...)4.对于文件系统缓存分配一半的物理内存文件系统缓存将被使用用于缓冲I/O操作。您应该确保将运行Elasticsearch的计算机的内存减少到文件系统缓存的至少一半。5.当使用自动生成的ids(auto-generatedids)索引具有显式ids的文档时,Elasticsearch需要检查同一个shard中是否已经存在具有相同id的文档,这是一个昂贵的操作,并且随着索引的增长和变得更贵。通过使用自动生成的ID,Elasticsearch可以跳过此检查,从而加快索引速度。6.购买更好的硬件搜索通常是I/O密集型的。这时候就需要为文件系统缓存分配更多的内存。使用SSD硬盘。使用本地存储(不要使用NFS、SMB等远程文件系统)亚马逊的弹性块存储(ElasticBlockStorage)也很出色。当然,与本地存储相比,还是慢了一些。如果您的搜索是CPU密集型的,请购买优质的CPU。7.如果你的节点只做大量的索引,增加索引缓冲区大小,确保index.memory.indexbuffersize足够大,每个分区最多可以提供512MB的索引缓冲区,索引的性能一般不会提高.Elasticsearch采用该设置(Java堆的百分比或绝对字节大小)并将其用作所有活动分片的共享缓冲区。非常活跃的分片自然会使用这个缓冲区,而不是执行轻量级索引的分片。默认值为10%,通常很多:例如,如果您为JVM提供10GB内存,它将为索引缓冲区提供1GB,这足以容纳两个索引繁重的分片。8.禁用fieldnames字段fieldnames字段引入了一些索引时间开销,所以如果你不需要运行存在性查询,你可能想要禁用它。(fieldnames:https://www.elastic.co/guide/...)9.对于其余部分,转到“调整磁盘使用情况”(https://www.elastic.co/guide/...)那里有许多磁盘使用策略也可以提高索引速度。第二部分——调整搜索速度1.文件系统缓存越大越好。为了使搜索更快,es非常依赖文件系统缓存。一般来说,至少需要一半的可用内存作为文件系统缓存,这样es才能在物理内存中维护索引2.用更好的硬件搜索一般是I/Obound。这时候就需要为文件系统缓存分配更多的内存。使用SSD硬盘,使用本地存储(不要使用NFS、SMB等远程文件系统)亚马逊的弹性块存储(ElasticBlockStorage)也很优秀。当然,与本地存储相比,还是慢了一些。如果您的搜索受CPU限制,请购买优质的CPU。3.文档模型(documentmodeling)文档需要使用合适的类型,这样搜索时操作消耗的资源更少。你做什么工作?答:避免连接操作。具体来说,嵌套会让查询慢好几倍。父子关系会让查询慢上百倍。如果不用加入也能解决问题,查询速度会快很多。优化索引数据的方式就是一个例子:所有文档都有一个价格字段,大多数查询在固定范围内运行范围聚合。您可以预先索引给定范围内的数据。那么,使用termsaggregation5.Mappings(最好使用keyword)数值类型的数据并不意味着一定要使用数值类型的字段。一般来说,存储标识符(ISBN,或数据库中标识记录的数字)的字段最好使用关键字(整数,long不好,亲爱的)。6.避免运行脚本一般来说,应该避免运行脚本。如果绝对需要它们,您应该使用painless和expressions引擎。7.在搜索圆角日期字段上使用now,一般不会缓存。但是roundeddate可以使用querycacheroundedtominutes等8.Forcedmergeread-onlyindexread-onlyindexcanbenefitfrom"mergingintoasinglelargesegment"9.Warmupglobalordinals(全局序数)globalordinals它用于在关键字字段上运行术语聚合。我不知道哪些字段将用于/不用于术语聚合,因此在需要时将全局序号加载到内存中。但是可以在映射类型上定义eagerglobalordinals==true,这样刷新的时候就会加载全局序号10。当机器在预热文件系统缓存后重新启动时,文件系统缓存将被清除。操作系统需要一段时间才能将索引的热点区域加载到文件系统缓存中。设置index.store.preload告诉操作系统这些文件需要提前加载到内存中11.使用索引排序来加速连接索引排序有助于以较慢的索引为代价来加速连接。在索引分类文档中阅读更多相关信息。12.使用偏好来优化缓存利用率有多种缓存可以帮助提高搜索性能,例如文件系统缓存、请求缓存或查询缓存。然而,所有这些缓存都是在节点级别维护的,这意味着如果你连续两次运行相同的请求,有一个或多个副本,并使用循环法(默认路由算法),那么这两个请求将转到不同的分片副本,阻塞节点级缓存帮助。由于搜索应用程序的用户通常会一个接一个地运行类似的请求,例如分析索引的一个狭窄子集,因此使用标识当前用户或会话的首选值可以帮助优化缓存使用。13.副本可能有助于吞吐量,但并非一直如此除了提高弹性之外,副本还可以帮助吞吐量。例如,如果你有一个单独的分片索引和三个节点,则需要将副本数设置为2,以便总共有3个分片副本来使用所有节点。现在假设您有一个2-shards索引和两个节点。在一种情况下,副本数为0,这意味着每个节点拥有一个分片。在第二种情况下,副本数为1,这意味着每个节点有两个分片。哪种设置在搜索性能方面表现最好?通常,每个节点的分片数量较少的设置会更好。原因是它增加了每个分片的可用文件系统缓存的份额,这可能是Elasticsearch的第一大性能因素。另外,请注意,如果单个节点发生故障,没有副本的设置将会失败,因此需要在吞吐量和可用性之间进行权衡。那么副本数是多少呢?如果你有一个有numnodes个节点的集群,那么numprimaries总计主分片,如果你希望能够一次处理maxfailures个节点故障,那么正确的副本数是max(maxfailures,ceil(numnodes/numprimaries)-1).14.开启自适应副本选择当存在多个数据副本时,elasticsearch可以使用一组称为自适应副本选择的标准,根据包含分片每个副本的节点的响应时间、服务时间和队列大小来选择最好的数据副本。这可以提高查询吞吐量并减少搜索密集型应用程序的延迟。第三部分:一些一般性的建议1.不要返回一个大的结果集ES被设计成一个搜索引擎,它非常擅长返回匹配查询的前n个文档。但是,数据库领域的工作,比如“返回所有满足某个查询的文档”,并不是es最擅长的领域。如果你真的需要返回所有文档,你可以使用ScrollAPI2,避免大文档。也就是说,考虑到(考虑到)http.maxcontextlength默认为==100MB,并且es拒绝索引100MB文档,如果单个文档更小会更好。当然你可以增加这个限制,但是Lucene本身有2GB的限制。即使不考虑上述限制,大文档也会对网络/内存/磁盘造成更大的压力;由于文件系统缓存的工作方式,任何搜索请求都需要get_id字段。即使不请求_source字段,获取大doc的_id字段也会消耗更大的index,大doc的内存消耗会是doc本身size的数倍。大型文档的邻近搜索和突出显示也更昂贵。它们的消耗直接取决于文档本身的大小。3.避免稀疏无关数据。不要将它们放在同一个索引中。规范化文档结构。避免稀疏字段上的类型。为什么禁用norms&doc_values属性稀疏性不好?Lucene背后的数据结构更擅长处理文本类型的紧凑型数据字段,默认启用规范;默认情况下启用数字、日期、ip、关键字和文档值。Lucene在内部使用整数docid来识别文档并与内部API交互。例如:当使用matchquery生成docid迭代器时,这些docid被用来获取它们的范数,以便计算分数。目前的实现是在每个doc中预留一个字节来存放范数值。获取norm值其实就是读取doc_id位置的一个字节,效率很高。Lucene可以通过这个值快速访问任何doc的norm值;然而,给定一个doc,即使某个字段没有值,仍然需要为doc的这个字段预留一个bytedocvalues也有同样的问题。pre-2.0fielddata被当前的docvalues取代。稀疏性最明显的影响是对存储的需求(任何文档的每个字段都需要一个字节);但是,稀疏性对索引速度和查询速度也有影响,因为:即使doc没有一些字段,但是在索引的时候,还是需要写这些字段,查询的时候,需要跳过的值这些领域。索引中有少量稀疏字段,完全没有问题。然而,这不应该成为常态。受稀疏性影响最大的是规范和文档值。不过倒排索引(用于索引文本和关键字字段)和二维点(用于索引geopoint字段)也会受到较小的影响。如何避免毛呢布料稀疏?1.不相关的数据不要放在同一个索引中,给个提示:如果索引小(也就是docs数量少),那么primaryshard也应该少。2.规范化文档结构(Normalizedocumentstructures)3.避免类型(Avoidmappingtype)对于同一个索引,最好只使用一种映射类型。同一个索引下,使用不同的映射类型来存储数据,听起来不错,但实际上并不好。鉴于(考虑)每种映射类型都会将数据存储在同一个索引中,因此,多个不同的映射类型,每个字段彼此不同,这也带来了稀疏性问题4.在稀疏字段上,禁用规范和文档值attributenorms用于计算score,没有score,可以禁用(所有过滤字段,可以禁用norms)docvlaues用于sort&aggregations,没有这两个,可以禁用但是,不要草率决定,因为norms&docvalues是不能修改的。OnlyreindexTip1:Mixingexactsearchandstemming(混合精确搜索和词干提取)对于搜索应用,词干提取是必须的。例如:查询skiing时,ski和skis都是预期结果。但是如果用户只想查询滑雪怎么办?解决办法是:使用多字段。同样的内容,用两种不同的方式索引存储query.simplequerystring.quotefieldsuffix,原来是查询精确匹配的秘诀2:获取一致的打分分数无法重现同一个请求,连续运行两次,但是两次以不一致的顺序返回文档。这是非常糟糕的用户体验。如果有复制品,可能会发生这种事情。这是因为:在搜索的时候,复制组中的分片是循环选择的,所以同一个请求跑了两次,而这个请求如果命中了复制组中不同的分片,两次的分数可能不一致。那么问题来了,你不是说primary和replica一直是in-sync,完全一致吗?为什么你点击了“同步,完全相同的分片”却计算出不同的分数?原因是文档被标记为“已删除”。如您所知,当更新或删除文档时,旧文档并没有被删除,而是被标记为“已删除”。只有在旧doc所在的segment被合并时,“deleted”doc才会从磁盘中删除索引统计(indexstatistic)是打分很重要的一环,但是由于deleteddoc的存在,索引统计在同一分片的不同副本(即:每个副本)上计算的是不一致的。个人理解:所谓的索引统计应该是df,即doc_freq索引统计是基于shard计算的。当然,“删除”的文档永远不会出现在结果集中。由于实际原因,“删除”的文档仍然包含在统计中假设分片A0刚刚完成了一个大的段合并,然后删除了许多“删除”的文档,而分片A1还没有执行段合并,所以A1仍然有那些“已删除”的文档。所以:两个请求命中了A0和A1,两者的索引统计有明显差异。如何避免无法复现乐谱的问题?在使用偏好查询参数发送搜索请求时,使用标识字符串来标识用户,并将标识字符串作为查询请求的偏好参数。这样可以保证同一个请求多次执行时,给定用户的请求总是到达同一个分片,所以得分会更加一致(当然,即使同一个分片,两个请求跨段合并,得分也会仍然不一致)这种方法还有另一个优点。当两个doc的score相同时,会默认按照doc的内部Lucenedocid排序(注意:这不是es中的_id或_uid)。但是,同一文档的内部Lucene文档ID在分片的不同副本之间可能不同。所以如果总是到达同一个shard,那么,两个score相同的doc,他们的顺序是一致的query没有排在第一位,这可能是sharding造成的。默认情况下,在搜索文档时,每个分片都会计算自己的分数。指数统计是另一个非常重要的评分因素。如果每个分片的索引统计数据相似,则搜索效果好的文档会在每个主分片中平均分配,因此索引统计数据将非常相似并且评分将按预期进行。但是,凡事都有一个问题:索引时使用路由(文档不能均分到每个primaryshard)查询多个索引。索引中的文档数量很少,这会导致:参与查询的每个分片都有自己的索引统计不相似(索引统计对最终得分影响巨大),因此打分错误(relevancy看起来不对),那么如何绕过scorewrong(Relevancy看起来不对)呢?如果数据集很小,只使用一个primaryshard(es默认是5个),这样两次查询的索引统计不会改变,所以分数会一致。另一种方式是将searchtype设置为:dfsquerythenfetech(默认为querythenfetch)dfsquerythen_fetch的作用是向所有相关分片发送请求,请求所有相关分片返回当前查询的索引统计信息。然后,协调节点将合并这些索引统计,得到合并后的统计协调节点。所有相关分片都需要执行查询阶段,所以提出请求,这时候还要带上mergedstatistics。这样,执行查询的分片将使用全局索引统计信息。在大多数情况下,要求所有相关分片返回当前查询的索引统计信息,这是非常廉价的。但是,如果查询包含非常多的字段/term查询,或者模糊查询,此时获取索引统计可能并不便宜,因为为了获取索引统计,term字典中的所有term可能需要再次查询英文原文:https://www.elastic.co/guide/...译者:GhostStories来源:http://wangnan.tech/post/elas...推荐近期文章:1.1,000+Java面试题及答案整理(2021最新版)2.别再满屏if/else了,试试策略模式,太爽了!!3.操!Java中xx≠null的新语法是什么?4、SpringBoot2.5发布,深色模式太炸了!5.《Java开发手册(嵩山版)》最新发布,赶快下载吧!感觉不错,别忘了点赞+转发!