现在越来越多海量的数据需要被传输,存储,管理和分析。只有那些具备分布式和并行处理能力的系统才能担此大任。而这其中就有一款开源产品叫Elasticsearch(以下简称ES)。
本文主要关注如何通过配置(包括索引维度,集群维度等)来优化ES的查询性能。
ES默认给每个索引分配5个主分片和1个副本分片。这样的配置并不能适配所有的业务场景,我们需要根据实际情况进行配置。
分片的大小对查询效率的影响是非常大的。如果一个索引上有很多非常小的分片,这些分片上的segment容易增长的过快最终超过限制。另外,大量小的分片也会影响查询的吞吐量,因为一个查询会从所有的分片上获取数据。
从另一方面讲,太大的分片也会影响搜索的性能,并且失败恢复的时候会耗时更长。ES官方建议单个分片的大小是20GB~40GB。举个例子,如果你评估你的索引大概是300GB的数据量,那么分片9到15个主分片是个好主意。如果你的集群有10个节点,可以考虑分配10个分片,这样主分片可以均匀的分布在每个节点上。
如果数据是持续不断的写入,使用时间维度的索引(比如每天一个索引)是个不错的主意。这样的话如果某段时间写入的吞吐量有变化我们也可以轻松的单独配置某个时间维度的索引。
如果以时间维度建立索引,那查询的时候如何从多个索引上检索数据呢?答案是使用索引别名。我们可以用一个别名关联多个时间维度的索引,然后查询的时候使用这个别名进行查询。不过需要注意性能问题,因为如果一个别名关联太多索引肯定会影响性能。比如说在集群配置允许的情况下,能建立以月为单位的索引就不要创建以星期为单位的索引。
一种应用场景是只关注最近发生的事情。ES有一种机制可以支持这种场景。文档在segment里面是有序的,我们只关注前面的一些文档而不是全部。ES有一个属性叫track_total_hits
,通过设置为false让ES做相关性查询的时候只关注最近的一些文档而不是全部,这样可以提高联合查询场景的效率。
分片采用的是分布式的设计架构,支持水平扩展。有两种类型的分片,一个是主分片负责读写,比如index,reindex,delete等操作。还有一种是副本分片,它的作用是负责高可用以及提高读的吞吐能力。
分片的大小,segment大小以及一个节点上该存放多少个有效的分片都是分片优化需要考虑的问题。
副本分片对于提高搜索能力至关重要,副本分片的数量也是需要关注的问题。一般建议分片总量是节点数的1.5到3倍。通常节点上的分片越少,文件系统缓存越是能在节点之间发挥优势。
没有人希望数据数据丢失,因此需要首先计算出发生故障的最大节点数。然后,根据索引的主分片数和节点数,计算副本分片的有效分布以获取较大的吞吐量。
ES集群其中一个最重要的配置是至少一半的物理内存留给文件系统缓存,这是ES热点索引的存放位置。
可用的堆内存也是需要考虑的,ES推荐在堆内存上最多20分片/GB。比如一个30GB堆内存的节点应该最多只包含600个分片,从而让ES维持一个比较健康的状态。
那么占用的磁盘空间的计算公式是:
20 * (Heap Size per GB) * (Size of Shard in GB)
一般情况下单个分片的大小是20到40GB,所以一个16G堆内存可以最大可以支持约12TB的磁盘空间。
关注这些限制可以让我们更好的设计集群以及规划未来。
使用基于时间的索引管理数据是不错的,一些时间比较久的索引如果没有写入的需求可以设置成只读,从而提高性能。
只读的索引可以使用force merge
减少索引上的segment数量,这样可以提高搜索性能。不过要记得不要在还需要更新的索引上使用该操作,否则会造成大segment。还有就是force merge
这个操作需要在业务低峰期进行,因为它非常耗时(主要是I/O)。
缓存也可以
The cache can be utilized for use-cases of the end-user. The preference setting can be used to optimize the usage of the caches since it will allow analyzing a narrower subset of the index.
通过设置bootstrap.memory_lock=true
内存交换可以被禁止。内存交换可能导致垃圾回收比较慢。(解释下:这个配置的意义是。锁定物理内存地址,防止es内存被交换出去,也就是避免es使用swap交换分区,频繁的交换,会导致IOPS变高)
在活跃的索引上,刷新间隔会一直增长。活跃的索引意味着数据在一直写入。默认的刷新时间是1秒,也就是ES每隔1秒会创建一个segment。可以根据不同的业务场景决定是否调大这个值,调大意味着较大的segment被创建,所以会减轻merge的压力。
index.merge.scheduler.max_thread_count
的默认设置是
Math.max(1, Math.min(4, Runtime.getRuntime().availableProcessors() / 2))
这个值控制并发的merge线程数,如果存储是并发性能较好的SSD,可以用系统默认配置,普通磁盘的话设为1。
ES有时候会在集群的节点之间对分片重新均衡,这个操作会影响查询的性能。可以通过把cluster.routing.rebalance.enable
设置为none
来关闭这个功能。
基于时间范围的查询,不应该以当前时间作为参数,因为当前时间是一直变化的,ES无法缓存这个查询结果,这样无法提高查询性能。
经常查询的一些字段可以使用copy-to
功能把这些字段组合在一起提高查询性能。比如汽车的商标名称,发动机版本,型号,颜色可以组合在一起。
大部分情况下集群中的节点上分片的分配是一样的,不过有时候会遇到一些节点硬件性能比较好,需要提高分配分片的权重。默认的权重值是0.45f。可以通过cluster.routing.allocation.balance.shard
来设置这个权重。
(Elasticsearch 在不违反 Allocation Decider 的情况下,尽量保证集群各个节点上的分片数是相近的。)
查询本身也会影响响应的延迟时间。(比如查询的数据过多)ES有个熔断机制,通过参数indices.breaker.total.limit
控制,这个值决定你的查询占据JVM堆内存的百分比到多少时会触发熔断,默认是70%。
ES默认假设主要的应用场景是搜索。为了提高并发能力,搜索的线程池设置需要提高,而写入的线程池可以降低。当然需要参考节点的CPU核数。
我们可以启用自适应副本选择来替换循环方式(round-robin)发送请求到数据副本,这允许协调节点基于以下标准将请求发送到被认为是“最佳”的副本:
1.协调节点和包含数据副本的节点之间的过去请求的响应时间
2.过去的搜索请求需要在包含数据的节点上执行
3.包含数据的节点上的搜索线程池的队列大小