Elasticsearch搜索调优权威指南 (1/3)

栏目: 后端 · 发布时间: 6年前

内容简介:本文首发于 vivo互联网技术 微信公众号英文原文:qbox.io/blog/elasti…作者:Adam Vanderbush

本文首发于 vivo互联网技术 微信公众号 mp.weixin.qq.com/s/qwkZKLb_g…

英文原文:qbox.io/blog/elasti…

作者:Adam Vanderbush

译者:杨振涛

目录

  1. 文档建模
  2. 全局序列号和延迟
  3. 多代关系
  4. 为文件系统缓存分配内存

Elasticsearch搜索调优权威指南,是QBOX在其博客上发布的系列文章之一,本文是该系列的第一篇,主要从文档建模、内存分配、文件系统缓存、GC和硬件等方面介绍了优化查询性能的一些经验。

Elasticsearch 5.0.0确实是在2.x之后的一个大版本,为大家带来了许多新东西。Elasticsearch现在作为Elastic Stack中的一员,与整个技术栈的其他产品的版本号已经对齐,现在Kibana、Logstash、Beats和Elasticsearch全都是5.0版本了。

这个版本的Elasticsearch是目前为止最快、最安全、最弹性,也是最易用的,而且还带来了很多的改进和新特性。

我们已经通过“Elasticsearch性能调优权威指南”系列,介绍了一些性能调优的基本经验和方法,解释了每一步最关键的系统设置和衡量指标。该系列共分下列3个部分:

  • The Authoritative Guide to Elasticsearch Performance Tuning (Part 1)
  • The Authoritative Guide to Elasticsearch Performance Tuning (Part 2)
  • The Authoritative Guide to Elasticsearch Performance Tuning (Part 3)

索引决策也很重要,它对如何搜索数据有很大的影响。如果是一个字符串字段,是否需要分词或归一化?如果是,怎么做?如果是一个数值型属性,需要哪种精度?还有很多其他类型,比如date-time、geospatial shape以及父子关系等,需要更多特别的考虑。

我们也通过一个系列教程讨论了“Elasticsearch索引性能优化”,介绍了一些通用的技巧和方法,来最大化索引的吞吐量并降低监控和管理的负载。该教程分如下3个部分:

本文旨在推荐一些搜索调优技术、策略以及Elasticsearch 5.0及以上的推荐特性。

Elasticsearch搜索调优权威指南 (1/3)

1.文档建模

内部对象属性数组并不像期望的那样工作。**Lucene **中没有内部对象的概念,所以Elasticsearch把对象层次展开到一个由属性名称和属性值组成的简单列表中。以下列文档为例:

curl -XPUT 'localhost:9200/my_index/my_type/1?pretty' -H 'Content-Type: application/json' -d '{
 "group" : "fans",
 "user" : [
   {
     "first" : "John",
     "last" :  "Smith"
   },
   {
     "first" : "Alice",
     "last" :  "White"
   }
 ]
}'
复制代码

该请求会在内部转换为如下的文档形式:

{
  "group" :        "fans",
  "user.first" : [ "alice", "john" ],
  "user.last" :  [ "smith", "white" ]
}
复制代码

如果需要索引对象数组,并维护数组中每个对象的依赖关系,应当使用内嵌数据类型而不是对象数据类型。内嵌对象在内部会把数组中的每个对象当作单独的隐藏文档来索引,即使用下述内嵌查询,可以单独查询每个内嵌对象:

curl -XPUT 'ES_HOST:ES_PORT/my_index?pretty' -H 'Content-Type: application/json' -d '{
 "mappings": {
   "my_type": {
     "properties": {
       "user": {
         "type": "nested"
       }
     }
   }
 }
}'

curl -XPUT 'ES_HOST:ES_PORT/my_index/my_type/1?pretty' -H 'Content-Type: application/json' -d '{
 "group" : "fans",
 "user" : [
   {
     "first" : "John",
     "last" :  "Smith"
   },
   {
     "first" : "Alice",
     "last" :  "White"
   }
 ]
}'


curl -XGET 'ES_HOST:ES_PORT/my_index/_search?pretty' -H 'Content-Type: application/json' -d '{
 "query": {
   "nested": {
     "path": "user",
     "query": {
       "bool": {
         "must": [
           { "match": { "user.first": "Alice" }},
           { "match": { "user.last":  "Smith" }}
         ]
       }
     }
   }
 }
}'

curl -XGET 'ES_HOST:ES_PORT/my_index/_search?pretty' -H 'Content-Type: application/json' -d '{
 "query": {
   "nested": {
     "path": "user",
     "query": {
       "bool": {
         "must": [
           { "match": { "user.first": "Alice" }},
           { "match": { "user.last":  "White" }}
         ]
       }
     },
     "inner_hits": {
       "highlight": {
         "fields": {
           "user.first": {}
         }
       }
     }
   }
 }
}'
复制代码

当有一个主实体比如一篇博客文章,带有一些有一定关系但又不是非常重要的其他实体比如评论时,内嵌对象会非常有用。如果能根据评论内容来查询到博客文章,那就很不错,而且内嵌查询和过滤器一起提供了更快的join查询能力。

内嵌对象模型的缺点如下:

为了 增加 、修改 或 删除 一个内嵌对象文档,整个文档必须重建索引;这就导致内嵌文档越多开销就越大。

搜索请求返回整个文档,而不是只返回匹配的内嵌文档。虽然已经以后计划支持返回根文档的部分最配内嵌文档,但目前仍然不支持。

有时候可能需要把主文档和其关联实体分离,这种分离由父子关系来提供。

通过建立另一个文档的父类型mapping,可以在相同索引的文档之间建立父子关系:

curl -XPUT 'ES_HOST:ES_PORT/my_index?pretty' -H 'Content-Type: application/json' -d '{
 "mappings": {
   "my_parent": {},
   "my_child": {
     "_parent": {
       "type": "my_parent"
     }
   }
 }
}'

curl -XPUT 'ES_HOST:ES_PORT/my_index/my_parent/1?pretty' -H 'Content-Type: application/json' -d '{
 "text": "This is a parent document"
}'

curl -XPUT 'ES_HOST:ES_PORT/my_index/my_child/2?parent=1&pretty' -H 'Content-Type: application/json' -d '{
 "text": "This is a child document"
}'

curl -XPUT 'ES_HOST:ES_PORT/my_index/my_child/3?parent=1&refresh=true&pretty' -H 'Content-Type: application/json' -d '{
 "text": "This is another child document"
}'

curl -XGET 'ES_HOST:ES_PORT/my_index/my_parent/_search?pretty' -H 'Content-Type: application/json' -d '{
 "query": {
   "has_child": {
     "type": "my_child",
     "query": {
       "match": {
         "text": "child document"
       }
     }
   }
 }
}'
复制代码

父子join对管理实体关系非常有用,尤其是在索引时间比检索时间很重要的情形下,但是它会带来较大的开销;父子查询比同等的内嵌查询要慢5到10倍。

2.全局序列号和延迟

父子关系使用了全局序列号来加速join操作。无论父子map是否使用了内存缓存或磁盘上的doc value,全局序列号仍然需要在索引发生任何改变时进行重建。

分片中的父代越多,全局序列号构建就越耗时。相对于需要父代和较少的子代, 父子关系最适合每个父代有很多子代的情形。

全局序列号默认是 延迟 构建:refresh后的第一个父子查询或聚合请求将会触发构建全局序列号。这会让用户感知到一个明显的潜在峰值。可以使用eager_global_ordinals 来把查询期构建全局序列号的成本转移到refresh期,通过如下方式mapping _parent属性:

curl -XPUT 'ES_HOST:ES_PORT/company -d ‘{
  "mappings": {
    "branch": {},
    "employee": {
      "_parent": {
        "type": "branch",
        "fielddata": {
          "loading": "eager_global_ordinals"
        }
      }
    }
  }
}’
复制代码

这里,_parent属性的全局序列号将会在一个新的段搜索可见时被构建。

对于很多的父代,全局序列号要花费数秒钟来构建。此时,需要增加refresh_interval,以便refresh的频率更低,而全局序列号保持可用的时间更长。这将大幅减少每秒钟重建全局序列号的CPU消耗。

3.多代关系

对多代数据的Join(参考Grandparents and Grandchildren)能力听起来很吸引人,但需要思考其代价:

  • Join越多,性能越差。
  • 每一个父代都需要把自己的string _id属性保存在内存,这可能会消耗大量RAM。
  • 当考虑关系型方案及父子关系是否适合时,可参考下列关于父子关系的建议:
  • 保守使用父子关系,仅当子代比父代多很多时才考虑。
  • 避免在单个查询中使用多父子关系来join。
  • 避免对使用has_child过滤器,或score_mode为 none 的has_child查询来打分。
  • 父ID尽量简短,以便在doc value中更好地压缩,从而在瞬时加载时消耗更少的内存。

4.为文件系统缓存分配内存

对于运行中Elasticsearch,内存是需要密切监控的重要资源之一。Elasticsearch和Lucene通过JVM堆内存和文件系统缓存两种方式来消耗内存。由于Elasticsearch运行在 Java 虚拟机(JVM)中,所以JVM的GC周期和频率也需要重点监控。

JVM堆内存

对于Elasticsearch一个“刚好合适”的JVM堆大小是非常重要的——不能设置过大或过小,原因见后文。一般来说Elasticsearch的经验值是分配少于50%的可用RAM给JVM堆,且不要超过32GB。

为Elasticsearch分配过少的堆内存,那么就会留给Lucene更多内存,而Lucene重度依赖于文件系统缓存来快速处理请求。不管怎样也不能设置过小的堆内存,因为当应用由于频繁GC而面临短时中断时,可能会遭遇内存溢出错误或吞吐量下降。

Elasticsearch默认安装时设置的JVM堆大小为1GB,这在大多数情况下都偏小。可以通过环境变量来设置期望的对大小并重启Elasticsearch:

export ES_HEAP_SIZE=10g
复制代码

设置JVM堆大小的另一种方式(相当于设置一样的最小值和最大值,以防止重新调整堆大小),是在每次启动Elasticsearch时通过命令行参数指定:

ES_HEAP_SIZE="10g" ./bin/elasticsearch
复制代码

这两种示例方式都是设置了10GB的堆大小,为了验证是否设置成功,执行:

curl -XGET http://ES_HOST:9200/_cat/nodes?h=heap.max
复制代码

返回的输出会显示已正确地更新了最大堆内存。

垃圾回收

Elasticsearch依靠GC过程来释放堆内存。由于GC本身也要消耗资源(为了释放资源!),所以应当留意GC频率和持续时间,以确认是否需要调整堆内存大小。设置过大的堆内存,换来的是更长的GC时间;这种过多的停顿非常危险,因为可能导致集群误认为该节点网络异常而失联。

因此,Elasticsearch重度依赖文件系统缓存来加速搜索。一般需要保证至少有一半的可用内存用于文件系统缓存,这样Elasticsearch才能保持索引数据的热点区域都在物理内存中。

使用更快的硬件

如果搜索受限于I/O,应当考虑为文件系统缓存分片更多内存(参考前文),或者购买更快的驱动。特别地,SSD公认地比机械磁盘性能好很多。尽可能使用本地存储,避免使用像 NFS 或 SMB 之类的远程或网络文件系统,也要注意像Amazon EBS这样的虚拟化存储。

Elasticsearch使用虚拟化存储工作是没有问题的,它因为快速和安装简单而受欢迎,但同样不幸的是,在基础上与专用的本地存储相比它天生就比较慢。如果在EBS上创建了一个索引库,请确认使用预分配的IOPS,否则很快就会被限流。

如果搜索受限于CPU,那么应当考虑购买更快的CPU。

更多内容敬请关注 vivo 互联网技术 微信公众号

Elasticsearch搜索调优权威指南 (1/3)

注:转载文章请先与微信号: labs2020 联系。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Learning JavaScript

Learning JavaScript

Shelley Powers / Oreilly & Associates Inc / 2006-10-17 / $29.99

As web browsers have become more capable and standards compliant, JavaScript has grown in prominence. JavaScript lets designers add sparkle and life to web pages, while more complex JavaScript has led......一起来看看 《Learning JavaScript》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

随机密码生成器
随机密码生成器

多种字符组合密码

MD5 加密
MD5 加密

MD5 加密工具