天天看点

elasticsearch 优化

批量提交

当有大量数据提交的时候,建议采用批量提交。比如在做 ELK 过程中 ,Logstash indexer 提交数据到 Elasticsearch 中 ,batch size 就可以作为一个优化功能点。但是优化 size 大小需要根据文档大小和服务器性能而定。像 Logstash 中提交文档大小超过 20MB ,Logstash 会请一个批量请求切分为多个批量请求。如果在提交过程中,遇到 EsRejectedExecutionException 异常的话,则说明集群的索引性能已经达到极限了。这种情况,要么提高服务器集群的资源,要么根据业务规则,减少数据收集速度,比如只收集 Warn、Error 级别以上的日志。

优化硬件

在经济压力能承受的范围下, 尽量使用固态硬盘 SSD。SSD 相对于机器硬盘,无论随机写还是顺序写,都较大的提升。

磁盘备份采用 RAID0。因为 Elasticsearch 在自身层面通过副本,已经提供了备份的功能,所以不需要利用磁盘的备份功能,同时如果使用磁盘备份功能的话,对写入速度有较大的影响。

增加Refresh间隔

为了提高索引性能,Elasticsearch 在写入数据时候,采用延迟写入的策略,即数据先写到内存中,当超过默认 1 秒 (index.refresh_interval)会进行一次写入操作,就是将内存中 segment 数据刷新到操作系统中,此时我们才能将数据搜索出来,所以这就是为什么 Elasticsearch 提供的是近实时搜索功能,而不是实时搜索功能。

内部系统对数据延迟要求不高的话,我们可以通过延长 refresh 时间间隔,可以有效的减少 segment 合并压力,提供索引速度。在做全链路跟踪的过程中,我们就将 index.refresh_interval 设置为 30s,减少 refresh 次数。同时,在进行全量索引时,可以将 refresh 次数临时关闭,即 index.refresh_interval 设置为 -1,数据导入成功后再打开到正常模式,比如 30s。

减少副本数量

Elasticsearch 默认副本数量为 3 个,虽然这样会提高集群的可用性,增加搜索的并发数,但是同时也会影响写入索引的效率。

在索引过程中,需要把更新的文档发到副本节点上,等副本节点生效后在进行返回结束。副本数量多的时候可以提高集群的可用性,增加搜索的并发数,但是同时也会影响写入索引的效率,使用 Elasticsearch 做业务搜索的时候,副本建立尽量结合业务特性,但是内部 ELK 日志系统、分布式跟踪系统中,完全可以将副本数目设置为 1 个。

查询性能优化:

路由:

当我们查询文档的时候,Elasticsearch 如何知道一个文档应该存放到哪个分片中呢 ?

       它其实是通过下面这个公式来计算出来

       shard = hash(routing) % number_of_primary_shards(routing 默认值是文档的 id,也可以采用自定义值,比如用户 id。)
           

不带 routing 查询:

在查询的时候因为不知道要查询的数据具体在哪个分片上,所以整个过程分为 2 个步骤

分发:请求到达协调节点后,协调节点将查询请求分发到每个分片上。

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

带 routing 查询:

查询的时候,可以直接根据 routing 信息定位到某个分配查询,不需要查询所有的分配,经过协调节点排序。向上面自定义的用户查询,如果 routing 设置为 userid 的话,就可以直接查询出数据来,效率提升很多

Filter VS Query:

尽可能使用过滤器上下文(Filter)替代查询上下文(Query),Elasticsearch 针对 Filter 查询只需要回答「是」或者「否」,不需要像 Query 查询一下计算相关性分数,同时 Filter 结果可以缓存。           

Query:此文档与此查询子句的匹配程度如何?

Filter:此文档和查询子句匹配吗?

大翻页:

在使用 Elasticsearch 过程中,应尽量避免大翻页的出现。正常翻页查询都是从 From 开始 Size 条数据,这样就需要在每个分片中查询打分排名在前面的 From + Size 条数据。协同节点收集每个分配的前 From + Size 条数据。协同节点一共会受到 N * ( From + Size )条数据,然后进行排序,再将其中 From 到 From + Size 条数据返回出去。

如果 From 或者 Size 很大的话,导致参加排序的数量会同步扩大很多,最终会导致 CPU 资源消耗增大。可以通过使用 Elasticsearch scroll 和 scroll-scan 高效滚动的方式来解决这样的问题

JVM 设置

标准的建议是把 50% 的可用内存作为 Elasticsearch 的堆内存,保留剩下的 50%。当然它也不会被浪费,Lucene 会很乐意利用起余下的内存,并且最大内存设置不要超过32GB(具体原因可以参考 连接)

JVM 还有一个配置 bootstrap.mlockall: true,比较重要。这是让 JVM 启动的时候就 锁定 heap 内存。

其他优化项:

hreadpool.search.queue_size 这个配置是很重要的(线程池相关),一般默认是够用了,可以尝试提高

max_num_segments 建议配置为1。虽然优化时间会变长,但是在高峰期前能完成的话,会对查询性能有很大好处

elasticsearch-段合并

由于自动刷新流程每秒会创建一个新的段 ,这样会导致短时间内的段数量暴增。而段数目太多会带来较大的麻烦。 每一个段都会消耗文件句柄、内存和cpu运行周期。更重要的是,每个搜索请求都必须轮流检查每个段;所以段越多,搜索也就越慢。

Elasticsearch通过在后台进行段合并来解决这个问题。小的段被合并到大的段,然后这些大的段再被合并到更大的段。

段合并的时候会将那些旧的已删除文档 从文件系统中清除。 被删除的文档(或被更新文档的旧版本)不会被拷贝到新的大段中。

启动段合并不需要你做任何事。进行索引和搜索时会自动进行。

1、 当索引的时候,刷新(refresh)操作会创建新的段并将段打开以供搜索使用。

2、 合并进程选择一小部分大小相似的段,并且在后台将它们合并到更大的段中。这并不会中断索引和搜索。

两个提交的段和两个未提交的段被合并到大段里面

Two commited segments and one uncommited segment in the process of being merged into a bigger segment

一旦合并结束,老的段被删除

合并大的段需要消耗大量的I/O和CPU资源,如果任其发展会影响搜索性能。Elasticsearch在默认情况下会对合并流程进行资源限制,所以搜索仍然 有足够的资源很好地执行

optimize API(强制合并 API )

它会将一个分片强制合并到 max_num_segments 参数指定大小的段数目。 这样做的意图是减少段的数量(通常减少到一个),来提升搜索性能。

例如在日志这种用例下,每天、每周、每月的日志被存储在一个索引中。 老的索引实质上是只读的;它们也并不太可能会发生变化,使用optimize优化老的索引,将每一个分片合并为一个单独的段就很有用了;这样既可以节省资源,也可以使搜索更加快速

合并索引中的每个分片为一个单独的段:

POST /logstash-2014-10/_optimize?max_num_segments=1