Elasticsearch性能调优
使用doc values来优化查询
缓存可以提高性能,但不是万能,有时候不使用缓存反而性能更好。如果数据频繁更新,并且查询具有唯一性且不可重复,那么缓存是不会起什么作用的,甚至是更糟糕。
字段缓存存在的问题
每个缓存都基于几个简单的原理。主要的设想就是为了提高性能,避免从较慢的类似机械硬盘的数据源获取数据,或者减少系统重新计算数据的需求,保存一部分数据到内存中是值得的。然而缓存不是免费的,有着代价。对于 ES 而言,缓存的代价主要是内存。根据缓存类型的不同,你可能只需要保存最近使用的数据,但是同样,这并不总能实现的。有时,容纳全部的信息是必须的,因为不然的话,缓存就毫无意义。在排序和聚合时使用的字段缓存,为了让缓存有作用,指定字段的所有值都必须被 ES 实例 univerted,并保存在缓存里。如果有大量文档,并且分片非常大,那么可能会遇到麻烦。会在查询返回的时候报告内存问题。 而出现这些问题的时候,doc values 是可以解决问题的方案。他是 Lucene 中基于列的数据结构。这意味着它们不将数据保存在倒排索引中,而是保存在一个基于文档的数据结构里,并保存在磁盘上,在索引文档时就计算好。于是他避免我们在字段缓存中保存 uninverted 的数据,代替的是 doc values 从索引中获取数据。在 ES 1.4 之后 doc values 的访问与使用字段缓存一样快。
使用doc values的例子
为了向你展示基于 doc values 和基于字段数据缓存在内存消耗上的不同,索引一些简单的文档到 ES 中。我们索引相同的数据到两个索引中,结构相同,唯一不同就是 "doc_values":true
。
了解垃圾回收器
Java内存
- eden space 是堆内存中 JVM 最初分配大多数类型的对象的部分
- survivor space 是保存从 eden 空间的垃圾回收中幸存下来的对象的部分,分为 survivor0 和 survivor1。
- tenured generation 是容纳在 survivor 空间里生存了一段时间的对象的部分。
- permanent generation 是非堆内存,存储虚拟机自身数据的地方,例如类和对象的方法都保存在这里。
- code cache 是非堆内存,它存在于 HotSpot JVM 中,用来编译和存储 native 代码。
以上分类,eden 和 survivor 空间被叫做年轻代堆空间,tenured 称为老年代。
解决GC问题
可以打开 ES 的 GC 日志,或者使用 jstat
命令。当然还有其他的 GC 监控工具。ES 允许我们观察垃圾回收器超长时工作的周期。
避免内存交换
内存交换是把内存也写入磁盘的过程,当物理内存的数量不够了,或者操作系统由于某些原因认为一部分 RAM 内存写入磁盘好些,就会发生内存交换。如果交换了内存页再次被需要,操作系统会从交换分区中加载他们,并允许进程使用他们。这个过程会消耗时间和资源。
当使用 ES,当然希望避免它的进程内存被交换。如果有部分 ES 使用的内存被写到磁盘上读取,这会影响搜索和索引的性能。ES 允许我们关闭它的内存交换,需要在 ES 的配置文件中设置 bootstrap.mlockall=true
。
对查询做基准测试
为基准测试配置集群
基准测试功能默认是关闭的,任何在正确配置的 ES 节点上使用基准测试的尝试都会导致一个类似下面的错误。报告 BenchmarkNodeMissingException。因为没人希望冒险在生产环境的集群上执行有潜在危险的功能,在进行性能测试和基准测试期间,你会执行许多复杂的和重度的查询,因此在真实用户使用的集群上执行这些基准测试似乎不是一个好主意,他会导致集群响应缓慢,可以造成系统崩溃和不好的用户体验。为了使用基准测试吗,需要告诉 ES 哪个节点能够运行需要测试的查询,可以在该实例启动时,设置 --node.bench true
。或者在配置文件中添加该配置信息。
进行基准测试
ES 提供了 _bench 的 REST 端点,允许在集群中的基准测试节点执行任务。可以设置不同的查询来比较,定义其查询名作为标识,多个查询组成一个竞争者列表。 基准测试会返回使用相同的请求数(5000)和并发数(5)。对比预热时间和统计信息,就可以轻易得到哪个查询更优的结论。
热点线程
集群比平时执行得缓慢,使用大量的 CPU 资源的时候,那么需要做什么让集群恢复正常呢?这句是使用热点线程 API 的场景。这里的热点线程是指一个 Java 线程,使用大量 CPU 并且执行了相当长的一段时间。/_nodes/hot_threads/
。也可以检查所有节点。
扩展ES
垂直扩展
垂直扩展有一个明显的限制,当服务器上的最大可用物理内存或者 JVM 需要使用的总内存,当你有足够多的数据和复杂的查询的时候,很快就会碰到内存问题,添加新的内存也帮不了你。 例如由于垃圾回收和无法使用压缩选项,以为这使用了标记相同的内存空间,JVM 需要使用2倍的内存,你不回想要给 JVM 分配超过31GB的物理内存。