阅读 1464

深入 Elastic Search: ES 性能优化总结

前言:拥抱新版本

要保证 ElasticSearch 的版本的升级,ElasticSearch 的每一次升级在特定场景下基本都有 10% - 30% 的性能提升,升级 ElasticSearch 的版本是代价很小的提升性能和编码体验的方式,如果不能保证小的稳定版本的更新,那起码要保证大版本的更新,尽量不要和官方差了一个多大版本还不升级,我就正在经历这样的事情,跨三个大版本升级实在是非常痛苦!

查询优化

SSD

ssd 好处不用说了,但是可能不是我们能决定的,这个知道就行。

合适的内存

ElasticSearch 是一个非常非常吃内存的搜索引擎,搜索过程中的 sort(排序)、agg(聚合),分词过程中的 fieldadd、倒排索引等等,一直在消耗着内存,一定要有足够的 JVM HEAP 在维持这种平衡。

官方明确表示:64G 内存的机器是比较理想的,JVM HEAP 最好设置为机器的 1/2 也就是 32G,保证足够的堆空间和足够的堆外内存,因为 Lucene 负责全文检索,Lucene 的性能取决于和操作系统的相互作用。如果你把所有的内存都分配给 Elasticsearch 的堆内存,那将不会有剩余的内存交给 Lucene。 这将严重地影响全文检索的性能,所以堆外内存不够也会经常发生 OOM。

  • 标准的建议是把 50% 的可用内存作为 Elasticsearch 的堆内存,保留剩下的 50%。当然它也不会被浪费,Lucene 会很乐意利用起余下的内存。

此外还要注意 ElasticSearch 的 32G 内存限制问题!直到今天官网还是不建议我们超过 32G 内存

堆内存:大小和交换 | Elasticsearch: 权威指南 | Elastic

问题的源头是:内存指针压缩(compressed oops)技术的瓶颈,简单说超过 32GB,指针压缩失效,内存浪费,坊间传言: 50GB 堆内存性能与 31GB 接近且垃圾回收压力大,也影响性能。

-Xms 31g
-Xmx 31g
复制代码

如果你有 128 的机器,你可以设置两个节点,每个分配 32 G 的内存组成集群。如果你是这种情况 (某一个物理机器(硬盘)犹豫内存瓶颈开了多个节点),那一定要设置

cluster.routing.allocation.same_shard.host: true
复制代码

这会防止同一个分片(shard)的主副本存在同一个物理机上(因为如果存在一个机器上,副本的高可用性就没有了)

_routing 路由

ElasticSearch 自带的 routing (数据路由) 很好用,它的使用场景类似 ShadingSpare(Apache 的分库分表中间件),routing 可以直接定位到分片,按照你们自己相关业务类型,自行决定按照哪个粒度、哪个字段决定整个索引的路由,一般是越均匀越好,笔者是做 SAAS 平台的,那么就可以使用(公司标识)来做 _routing,如果是日志系统可以考虑使用(时间粒度)作为 _routing ,数据量大加了 _routing 之后的查询速度大大提升。

如果不加 _routing ElasticSearch 内部流程:

  • 分发:请求到达协调节点后,协调节点将查询请求分发到每个分片。
  • 聚合: 协调节点搜集到每个分片上查询结果,在将查询的结果进行排序,之后给用户返回结果。

数据大的情况,查询任何数据每次查询都要带上 _routing ,特殊的业务场景需要不分 _routing 的可以使用统一的 _routing 处理。

不涉及搜索的字段不要分词

有很多业务字段是根本不需要分词的,比如 关联 ID、某些不参与搜索的业务字段、状态码等,这样的字段一定要声明不分词:

index:"no_analyzer" 或 type:"keyword"
复制代码

进行全量匹配,这样这个字段不参与分词,不参与倒排索引、减少segment 节省内存空间,让内存空间缓存更多更有用的数据,内存是有限的,要珍惜使用

  • 查看索引的 segment memory 占用情况
GET _cat/segments/indexName?v
复制代码

size : segment 占用的磁盘空间

memory.size: segment 占用的内存空间

索引拆分

首先不要再在一个索引下创建多个type!即便你是 2.x, 5.x 这种还没有禁止多个 type 的版本,考虑到未来版本升级等后续的可扩展性,你也需要尽快拆解出来,而且在我大量参考官网的时候我得知:一个 Index 创建多个 Type 会有数据倾斜的问题,有点类似哈希环的倾斜,这样会导致数据分布不均匀,文档压缩性能差!而分布密集,更符合 Lucene 规范,更适合压缩。每个索引默认对应一个 _doc 即可。

其次,若是类似基于 ELK 、ELFK 的日志系统,可以使用 rollover + index template 按照时间滚动索引,避免一个 index 和 固定的 shard 产生巨量的数据,导致搜索的灾难。一个索引百亿 doc 设置 5个 shards,可以保证不浪费分片又能保证实时性。

不允许深度分页

这个基本大型网站都不允许,当然很多产品经理很 ** ,不信你去淘宝搜搜商品,最多 100 页,这也是我说服产品经理的方式,而且 ElasticSearch 默认是查询前 10000 条最 match 的数据,如果你想更深入的查询(比如:报表计算需要全量的数据),可能需要 maxResult 或者 scroll 来进行一次性读取,或翻页查询。

禁用 wildcard

到现在我们还有 wildcard 的隐患没有调整:

{
            "bool": {
              "must": [
                {
                  "wildcard": {
                    "name": "${name}"
                  }
                },
                {
                  "term": {
                    "companyId": "${companyId}"
                  }
                }
              ]
            }
 }
复制代码

随便贴个例子:discuss.elastic.co/t/wildcard-…

wildcard 是性能杀手,在数据量较大的情况,他会查询的非常非常慢,很容易出现卡死,甚至导致集群节点崩溃宕机的情况。

替代:standard和ik结合,使用match_phrase检索。nGram 自定义分词器,使用match_phrase检索,或者探讨是否能使用精度差一些的搜索方式?通过 match_phrase 和 slop 结合查询。

为了满足通配符查询,它必须遍历所有项,这会导致大量的处理开销。这玩应也就是能用,但是绝对不好用。

  • 可以使用 search.allow_expensive_queries = false 禁止 wildcard,不让团队任何人使用。

数据预热

可能经常有热点关键词被搜索,当搜索的时候相关的 segment 索引还没有在内存上,这样就需要一个从 磁盘 >>> 缓存 这样的一个过程,这个过程时间是比直接查询内存要慢得多得多的,然而过了一段时间 内存可能又被其他索引的 segment 替换了,所以下次搜索又需要这样一个漫长的过程:

因为 ElasticSearch 的内存是有限的,我们可以想象它内存中的数据就像是一个被 LRU 算法管理的固定大小的空间,我们可以想办法隔一段时间触发热点的数据搜索,让它始终在 ES 内存(JVM HEAP)中存在。

写入优化

日志最好用 Rollover

相对于日志二燕,可以使用 rollover + index template 按照时间滚动索引,避免一个 index 和 固定的 shard 产生巨量的数据,导致搜索的灾难。一个索引百亿 doc 设置 5个 shards,可以保证不浪费分片又能保证实时性。

而基于 ELK 、ELFK 的日志系统,就更简单了,已经为我们提供好了规则,配置一下就可以了。

减少 Refresh 的次数

ES 是近实时引擎,并非实时,默认 1S,因为Lucene 将待写入的数据先写到内存中,超过 1 秒(默认)时就会触发一次 Refresh,然后 Refresh 会把内存中的的数据刷新到操作系统的文件缓存系统中。

某些场景若是对于搜索的实时性不是很高,比如:午夜生成的报表,日志 等,可以考虑加大 refresh 默认 1S 的事件,减少 Segment Merge 的次数.

index.refresh_interval:20s
复制代码

每个目录挂载不同的磁盘

这个场景可以在上述的 32G 内存瓶颈中体现,128G 内存的服务器分了 2 个 node,在data目录下,那我们分出 2 个目录,分别挂载到不同的硬盘上去(一定要是不同的物理硬盘,不是分区......)这相当于做了raid0, 能大大的提高写入速度,当然不是无限制的还是有网卡带宽瓶颈的,若云服务是机械硬盘这样做有点意义,SSD 大可不必。

  • 前提条件是一台机器、不同节点、不同硬盘

这样的方式对于其他分布式存储系统都通用。

Github 首发归档整理:github.com/pkwenda/Blo…