关于java:30-个-ElasticSearch-调优知识点都给你整理好了

45次阅读

共计 8043 个字符,预计需要花费 21 分钟才能阅读完成。

ES 官网调优指南

第一局部:调优索引速度

第二局部:调优搜寻速度

第三局部:通用的一些倡议

ES 公布时带有的默认值,可为 es 的开箱即用带来很好的体验。全文搜寻、高亮、聚合、索引文档 等性能无需用户批改即可应用, 当你更分明的晓得你想如何应用 es 后,你能够作很多的优化以进步你的用例的性能, 上面的内容通知你 你应该 / 不应该 批改哪些配置。

第一局部:调优索引速度

https://www.elastic.co/guide/…

应用批量申请批量申请将产生比单文档索引申请好得多的性能。

为了晓得批量申请的最佳大小,您应该在具备单个分片的单个节点上运行基准测试。首先尝试索引 100 个文件,而后是 200,而后是 400,等等。当索引速度开始稳固时,您晓得您达到了数据批量申请的最佳大小。在配合的状况下,最好在太少而不是太多文件的方向上犯错。请留神,如果群集申请太大,可能会使群集受到内存压力,因而倡议防止超出每个申请几十兆字节,即便较大的申请看起来成果更好。

发送端应用多 worker/ 多线程向 es 发送数据 发送批量申请的单个线程不太可能将 Elasticsearch 群集的索引容量最大化。为了应用集群的所有资源,您应该从多个线程或过程发送数据。除了更好地利用集群的资源,这应该有助于升高每个 fsync 的老本。

请确保留神 TOOMANYREQUESTS(429)响应代码(Java 客户端的 EsRejectedExecutionException),这是 Elasticsearch 告诉您无奈跟上以后索引速率的形式。产生这种状况时,应该再次尝试暂停索引,现实状况下应用随机指数回退。

与批量调整大小申请相似,只有测试能力确定最佳的 worker 数量。这能够通过逐步减少工作者数量来测试,直到集群上的 I / O 或 CPU 饱和。

1. 调大 refresh interval

默认的 index.refresh_interval 是 1s,这迫使 Elasticsearch 每秒创立一个新的分段。减少这个价值(比如说 30s)将容许更大的局部 flush 并缩小将来的合并压力。

2. 加载大量数据时禁用 refresh 和 replicas

如果您须要一次加载大量数据,则应该将 index.refreshinterval 设置为 - 1 并将 index.numberofreplicas 设置为 0 来禁用刷新。这会临时使您的索引处于危险之中,因为任何分片的失落都将导致数据 失落,然而同时索引将会更快,因为文档只被索引一次。初始加载实现后,您能够将 index.refreshinterval 和 index.numberofreplicas 设置回其原始值。

3. 设置参数,禁止 OS 将 es 过程 swap 进来

您应该确保操作系统不会 swapping out the java 过程,通过禁止 swap(https://www.elastic.co/guide/…)

4. 为 filesystem cache 调配一半的物理内存

文件系统缓存将用于缓冲 I / O 操作。您应该确保将运行 Elasticsearch 的计算机的内存至多缩小到文件系统缓存的一半。

5. 应用主动生成的 id(auto-generated ids)

索引具备显式 id 的文档时,Elasticsearch 须要查看具备雷同 id 的文档是否曾经存在于雷同的分片中,这是低廉的操作,并且随着索引增长而变得更加低廉。通过应用主动生成的 ID,Elasticsearch 能够跳过这个查看,这使索引更快。

6. 买更好的硬件

搜寻个别是 I /O 密集的,此时,你须要

  1. 为 filesystem cache 调配更多的内存
  2. 应用 SSD 硬盘
  3. 应用 local storage(不要应用 NFS、SMB 等 remote filesystem)
  4. 亚马逊的 弹性块存储(Elastic Block Storage)也是极好的,当然,和 local storage 比起来,它还是要慢点

如果你的搜寻是 CPU- 密集的,买好的 CPU 吧

7. 加大 indexing buffer size

如果你的节点只做大量的索引,确保 index.memory.indexbuffersize 足够大,每个分区最多能够提供 512 MB 的索引缓冲区,而且索引的性能通常不会进步。Elasticsearch 采纳该设置(java 堆的一个百分比或相对字节大小),并将其用作所有流动分片的共享缓冲区。十分沉闷的碎片天然会应用这个缓冲区,而不是执行轻量级索引的碎片。

默认值是 10%,通常很多:例如,如果你给 JVM 10GB 的内存,它会给索引缓冲区 1GB,这足以承载两个索引很重的分片。

8. 禁用 fieldnames 字段

fieldnames 字段引入了一些索引工夫开销,所以如果您不须要运行存在查问,您可能须要禁用它。(fieldnames:https://www.elastic.co/guide/…)

9. 剩下的,再去看看“调优 磁盘应用”吧

(https://www.elastic.co/guide/…)中有许多磁盘应用策略也进步了索引速度。

第二局部 - 调优搜寻速度

1.filesystem cache 越大越好

为了使得搜寻速度更快,es 重大依赖 filesystem cache

一般来说,须要至多一半的 可用内存 作为 filesystem cache,这样 es 能够在物理内存中 保有 索引的热点区域(hot regions of the index)

2. 用更好的硬件

搜寻个别是 I /O bound 的,此时,你须要

  • 为 filesystem cache 调配更多的内存
  • 应用 SSD 硬盘
  • 应用 local storage(不要应用 NFS、SMB 等 remote filesystem)
  • 亚马逊的 弹性块存储(Elastic Block Storage)也是极好的,当然,和 local storage 比起来,它还是要慢点

如果你的搜寻是 CPU-bound,买好的 CPU 吧

3. 文档模型(document modeling)

文档须要应用适合的类型,从而使得 search-time operations 耗费更少的资源。咋作呢?答:防止 join 操作。具体是指

  • nested 会使得查问慢 好几倍
  • parent-child 关系 更是使得查问慢几百倍

如果 无需 join 能解决问题,则查问速度会快很多

4. 预索引 数据

依据“搜寻数据最罕用的形式”来最优化索引数据的形式

举个例子:所有文档都有 price 字段,大部分 query 在 fixed ranges 上运行 range aggregation。你能够把给定范畴的数据 事后索引下。而后,应用 terms aggregation

5.Mappings(能用 keyword 最好了)

数字类型的数据,并不意味着肯定非得应用 numeric 类型的字段。

一般来说,存储标识符的 字段(书号 ISBN、或来自数据库的 标识一条记录的 数字),应用 keyword 更好(integer,long 不好哦,亲)

6. 防止运行脚本

一般来说,脚本应该防止。如果他们是相对须要的,你应该应用 painless 和 expressions 引擎。

7. 搜寻 rounded 日期

日期字段上应用 now,一般来说不会被缓存。但,rounded date 则能够利用上 query cache

rounded 到分钟等

8. 强制 merge 只读的 index

只读的 index 能够从“merge 成 一个独自的 大 segment”中收益

9. 预热 全局序数(global ordinals)

全局序数 用于 在 keyword 字段上 运行 terms aggregations

es 不晓得 哪些 fields 将 用于 / 不用于 term aggregation,因而 全局序数 在须要时才加载进内存

但,能够在 mapping type 上,定义 eagerglobalordinals==true,这样,refresh 时就会加载 全局序数

10. 预热 filesystem cache

机器重启时,filesystem cache 就被清空。OS 将 index 的热点区域(hot regions of the index)加载进 filesystem cache 是须要破费一段时间的。

设置 index.store.preload 能够告知 OS 这些文件须要提前加载进入内存

11. 应用索引排序来减速连贯

索引排序对于以较慢的索引为代价来放慢连贯速度十分有用。在索引分类文档中浏览更多对于它的信息。

12. 应用 preference 来优化高速缓存利用率

有多个缓存能够帮忙进步搜寻性能,例如文件系统缓存,申请缓存或查问缓存。然而,所有这些缓存都保护在节点级别,这意味着如果间断运行两次雷同的申请,则有一个或多个正本,并应用循环(默认路由算法),那么这两个申请将转到不同的分片正本,阻止节点级别的缓存帮忙。

因为搜寻应用程序的用户一个接一个地运行相似的申请是常见的,例如为了剖析索引的较窄的子集,应用标识以后用户或会话的优选值能够帮忙优化高速缓存的应用。

13. 正本可能有助于吞吐量,但不会始终存在

除了进步弹性外,正本能够帮忙进步吞吐量。例如,如果您有单个分片索引和三个节点,则须要将正本数设置为 2,以便共有 3 个分片正本,以便应用所有节点。

当初假如你有一个 2 -shards 索引和两个节点。在一种状况下,正本的数量是 0,这意味着每个节点领有一个分片。在第二种状况下,正本的数量是 1,这意味着每个节点都有两个碎片。哪个设置在搜寻性能方面体现最好?通常状况下,每个节点的碎片数少的设置将会更好。

起因在于它将可用文件系统缓存的份额进步到了每个碎片,而文件系统缓存可能是 Elasticsearch 的 1 号性能因子。同时,要留神,没有正本的设置在产生单个节点故障的状况下会呈现故障,因而在吞吐量和可用性之间进行衡量。

那么复制品的数量是多少?如果您有一个具备 numnodes 节点的群集,那么 numprimaries 总共是主分片,如果您心愿可能一次解决 maxfailures 节点故障,那么正确的正本数是 max(maxfailures,ceil(numnodes / numprimaries)– 1)。

14. 关上自适应正本抉择

当存在多个数据正本时,elasticsearch 能够应用一组称为自适应正本抉择的规范,依据蕴含分片的每个正本的节点的响应工夫,服务工夫和队列大小来抉择数据的最佳正本。这能够进步查问吞吐量并缩小搜寻量大的应用程序的提早。

第三局部:通用的一些倡议

1、不要 返回大的后果集

es 设计来作为搜索引擎,它十分善于返回匹配 query 的 top n 文档。但,如“返回满足某个 query 的 所有文档”等数据库畛域的工作,并不是 es 最善于的畛域。如果你的确须要返回所有文档,你能够应用 Scroll API

2、防止 大的 doc。即,单个 doc 小了 会更好

given that(思考到) http.maxcontextlength 默认 ==100MB,es 回绝索引操作 100MB 的文档。当然你能够进步这个限度,但,Lucene 自身也有限度的,其为 2GB 即便不思考下面的限度,大的 doc 会给 network/memory/disk 带来更大的压力;

  • 任何搜寻申请,都须要获取 _id 字段,因为 filesystem cache 工作形式。即便它不申请 _source 字段,获取大 doc _id 字段耗费更大
  • 索引大 doc 时耗费内存会是 doc 自身大小 的好几倍
  • 大 doc 的 proximity search, highlighting 也更加低廉。它们的耗费间接取决于 doc 自身的大小

3、防止 稠密

  • 不相干数据 不要 放入同一个索引
  • 一般化文档构造(Normalize document structures)
  • 防止类型
  • 在 稠密 字段上,禁用 norms & doc_values 属性

稠密为什么不好?

Lucene 背地的数据结构 更善于解决 紧凑的数据

text 类型的字段,norms 默认开启;numerics, date, ip, keyword,docvalues 默认开启 Lucene 外部应用 integer 的 docid 来标识文档 和 外部 API 交互。

举个例子:应用 match 查问时生成 docid 的迭代器,这些 docid 被用于获取它们的 norm,以便计算 score。以后的实现是每个 doc 中保留一个 byte 用于存储 norm 值。获取 norm 值其实就是读取 doc_id 地位处的一个字节

这十分高效,Lucene 通过此值能够快速访问任何一个 doc 的 norm 值;但,给定一个 doc,即便某个 field 没有值,仍须要为此 doc 的此 field 保留一个字节

docvalues 也有同样的问题。2.0 之前的 fielddata 被当初的 docvalues 所代替了。

稠密性 最显著的影响是 对存储的需要(任何 doc 的每个 field,都须要一个 byte);然而呢,稠密性 对 索引速度和查问速度 也是有影响的,因为:即便 doc 并没有某些字段值,但,索引时,仍然须要写这些字段,查问时,须要 skip 这些字段的值

某个索引中领有大量稠密字段,这齐全没有问题。但,这不应该成为常态

稠密性影响最大的是 norms&docvalues,但,倒排索引(用于索引 text 以及 keyword 字段),二维点(用于索引 geopoint 字段)也会受到较小的影响

如何防止稠密呢?

1、不相干数据 不要 放入同一个索引 给个 tip:索引小(即:doc 的个数较少),则,primary shard 也要少

2、一般化文档构造(Normalize document structures)

3、防止类型(Avoid mapping type)同一个 index,最好就一个 mapping type。在同一个 index 上面,应用不同的 mapping type 来存储数据,听起来不错,但,其实不好。given that(思考到) 每一个 mapping type 会把数据存入 同一个 index,因而,多个不同 mapping type,各个的 field 又互不雷同,这同样带来了稠密性 问题

4、在 稠密 字段上,禁用 norms & doc_values 属性

  • norms 用于计算 score,无需 score,则能够禁用它(所有 filtering 字段,都能够禁用 norms)
  • docvlaues 用于 sort&aggregations,无需这两个,则能够禁用它 然而,不要草率的做出决定,因为 norms&docvalues 无奈批改。只能 reindex

秘诀 1:混合 准确查问和提取词干(mixing exact search with stemming)

对于搜寻利用,提取词干(stemming)都是必须的。例如:查问 skiing 时,ski 和 skis 都是冀望的后果

但,如果用户就是要查问 skiing 呢?

解决办法是:应用 multi-field。同一份内容,以两种不同的形式来索引存储 query.simplequerystring.quotefieldsuffix,居然是 查问齐全匹配的

秘诀 2:获取一致性的打分

score 不能重现 同一个申请,间断运行 2 次,但,两次返回的文档程序不统一。这是相当坏的用户体验

如果存在 replica,则就可能产生这种事,这是因为:search 时,replication group 中的 shard 是按 round-robin 形式来抉择的,因而两次运行同样的申请,申请如果打到 replication group 中的不同 shard,则两次得分就可能不统一

那问题来了,“你不是终日说 primary 和 replica 是 in-sync 的,是完全一致的”嘛,为啥打到“in-sync 的,完全一致的 shard”却算出不同的得分?

起因就是标注为“已删除”的文档。如你所知,doc 更新或删除时,旧 doc 并不删除,而是标注为“已删除”,只有等到 旧 doc 所在的 segment 被 merge 时,“已删除”的 doc 才会从磁盘删除掉

索引统计(index statistic)是打分时十分重要的一部分,但,因为 deleted doc 的存在,在同一个 shard 的不同 copy(即:各个 replica)上 计算出的 索引统计 并不统一

集体了解:

  • 所谓 索引统计 应该就是 df,即 doc_freq
  • 索引统计 是基于 shard 来计算的

搜寻时,“已删除”的 doc 当然是 永远不会 呈现在 后果集中的 索引统计时,for practical reasons,“已删除”doc 仍然是统计在内的

假如,shard A0 刚刚实现了一次较大的 segment merge,而后移除了很多“已删除”doc,shard A1 尚未执行 segment merge,因而 A1 仍然存在那些“已删除”doc

于是:两次申请打到 A0 和 A1 时,两者的 索引统计 是显著不同的

如何躲避 score 不能重现 的问题?应用 preference 查问参数

收回搜寻申请时候,用 标识字符串 来标识用户,将 标识字符串 作为查问申请的 preference 参数。这确保屡次执行同一个申请时候,给定用户的申请总是达到同一个 shard,因而得分会更为统一(当然,即便同一个 shard,两次申请 跨了 segment merge,则仍然会得分不统一)

这个形式还有另外一个长处,当两个 doc 得分统一时,则默认按着 doc 的 外部 Lucene doc id 来排序(留神:这并不是 es 中的 _id 或 _uid)。然而呢,shard 的不同 copy 间,同一个 doc 的 外部 Lucene doc id 可能并不相同。因而,如果总是达到同一个 shard,则,具备雷同得分的两个 doc,其程序是统一的

score 错了

score 错了(Relevancy looks wrong)

如果你发现

  • 具备雷同内容的文档,其得分不同
  • 齐全匹配 的查问 并没有排在第一位 这可能都是由 sharding 引起的
  • 默认状况下,搜寻文档时,每个 shard 本人计算出本人的得分。
  • 索引统计 又是打分时一个十分重要的因素。

如果每个 shard 的 索引统计类似,则 搜寻工作的很好

文档是平分到每个 primary shard 的,因而 索引统计 会十分类似,打分也会按着预期工作。但,万事都有个然而:

  • 索引时应用了 routing(文档不能平分到每个 primary shard 啦)
  • 查问多个索引
  • 索引中文档的个数 非常少

这会导致:参加查问的各个 shard,各自的 索引统计 并不类似(而,索引统计对 最终的得分 又影响微小),于是 打分出错了(relevancy looks wrong)

那,如何绕过 score 错了(Relevancy looks wrong)?

如果数据集较小,则,只应用一个 primary shard(es 默认是 5 个),这样两次查问 索引统计 不会变动,因此得分也就统一啦

另一种形式是,将 searchtype 设置为:dfsquerythenfetech(默认是 querythenfetch)

dfsquerythen_fetch 的作用是

  • 向 所有相干 shard 发出请求,要求 所有相干 shard 返回针对以后查问的 索引统计
  • 而后,coordinating node 将 merge 这些 索引统计,从而失去 merged statistics
  • coordinating node 要求 所有相干 shard 执行 query phase,于是 发出请求,这时,也带上 merged statistics。这样,执行 query 的 shard 将应用 全局的索引统计

大部分状况下,要求 所有相干 shard 返回针对以后查问的 索引统计,这是十分 cheap 的。但,如果查问中 蕴含 十分大量的 字段 /term 查问,或者有 fuzzy 查问,此时,获取 索引统计 可能并不 cheap,因为 为了失去 索引统计 可能 term dictionary 中 所有的 term 都须要被查问一遍

英文原文:https://www.elastic.co/guide/…

译者:Ghost Stories

起源:http://wangnan.tech/post/elas…

近期热文举荐:

1.1,000+ 道 Java 面试题及答案整顿 (2021 最新版)

2. 别在再满屏的 if/ else 了,试试策略模式,真香!!

3. 卧槽!Java 中的 xx ≠ null 是什么新语法?

4.Spring Boot 2.5 重磅公布,光明模式太炸了!

5.《Java 开发手册(嵩山版)》最新公布,速速下载!

感觉不错,别忘了顺手点赞 + 转发哦!

正文完
 0