分析&回答
1)GC 方面
- 优化gc,减少gc时间。某个节点gc时间过长,master ping3次(zen discovery默认ping失败重试3次)不通后就会把该节点剔除出集群,从而导致索引进行重新分配。
- 调大zen discovery的重试次数(es参数:ping_retries)和超时时间(es参数:ping_timeout)。后来发现根本原因是有个节点的系统所在硬盘满了。导致系统性能下降。
2)索引方面
按着分词效率和业务契合度的分词器。优化segment(段)。
3)分片
- 按着自己的业务需求控制shard数量,shard过多,交互多。shard过少,单个分片数据容易累积,当单个分片的数据超过30G的时候就有性能下降的情况,当超过50G的时候,就有明显性能下降的效果。
- 采用热分离:当数据大小即将超过30G的时候。可以把以前的数据转存到其他容器里面去。比如hdfs。
4)副本
副本太多性能下降
5)查询优化
- 查询走缓存(打分用query,不打分用filter) 参考 Elasticsearch 中 Query与Filter区别
- 不要返回较大的结果集,ES只擅长返回匹配查询较少文档,如果需要返回非常多的文档需要使用Scroll。
6)增加刷新间隔
index.refresh_interval默认是1s,可以改成30s以减少合并压力。
反思&扩展
集群方面的优化,选择性了解:
- 64 GB 内存的机器是非常理想的, 但是32 GB 和16 GB 机器也是很常见的。少于8 GB 会适得其反。
- 如果你要在更快的 CPUs 和更多的核心之间选择,选择更多的核心更好。多个内核提供的额外并发远胜过稍微快一点点的时钟频率。
- 如果你负担得起 SSD,它将远远超出任何旋转介质。 基于 SSD 的节点,查询和索引性能都有提升。如果你负担得起,SSD 是一个好的选择。
- 即使数据中心们近在咫尺,也要避免集群跨越多个数据中心。绝对要避免集群跨越大的地理距离。
- 请确保运行你应用程序的 JVM 和服务器的 JVM 是完全一样的。 在 Elasticsearch 的几个地方,使用 Java 的本地序列化。
- 通过设置gateway.recover_after_nodes、gateway.expected_nodes、gateway.recover_after_time可以在集群重启的时候避免过多的分片交换,这可能会让数据恢复从数个小时缩短为几秒钟。
- Elasticsearch 默认被配置为使用单播发现,以防止节点无意中加入集群。只有在同一台机器上运行的节点才会自动组成集群。最好使用单播代替组播。
- 不要随意修改垃圾回收器(CMS)和各个线程池的大小。
- 把你的内存的(少于)一半给 Lucene(但不要超过 32 GB!),通过ES_HEAP_SIZE 环境变量设置。
- 内存交换到磁盘对服务器性能来说是致命的。如果内存交换到磁盘上,一个 100 微秒的操作可能变成 10 毫秒。 再想想那么多 10 微秒的操作时延累加起来。 不难看出 swapping 对于性能是多么可怕。
- Lucene 使用了大量的文件。同时,Elasticsearch 在节点和 HTTP 客户端之间进行通信也使用了大量的套接字。 所有这一切都需要足够的文件描述符。你应该增加你的文件描述符,设置一个很大的值,如 64,000。
补充:索引阶段性能提升方法
- 使用批量请求并调整其大小:每次批量数据 5–15 MB 大是个不错的起始点。
- 存储:使用 SSD
- 段和合并:Elasticsearch 默认值是 20 MB/s,对机械磁盘应该是个不错的设置。如果你用的是 SSD,可以考虑提高到 100–200 MB/s。如果你在做批量导入,完全不在意搜索,你可以彻底关掉合并限流。另外还可以增加 index.translog.flush_threshold_size 设置,从默认的 512 MB 到更大一些的值,比如 1 GB,这可以在一次清空触发的时候在事务日志里积累出更大的段。
- 如果你的搜索结果不需要近实时的准确度,考虑把每个索引的index.refresh_interval 改到30s。
- 如果你在做大批量导入,考虑通过设置index.number_of_replicas: 0 关闭副本。
喵呜面试助手: 一站式解决面试问题,你可以搜索微信小程序 [喵呜面试助手] 或关注 [喵呜刷题] -> 面试助手 免费刷题。如有好的面试知识或技巧期待您的共享!