本文首发于 vivo互联网技术 微信公众号 https://mp.weixin.qq.com/s/qwkZKLb_ghmlwrqMkqlb7Q
英文原文:https://qbox.io/blog/elasticsearch-search-tuning-5-0-ultimate-guide
作者: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及以上的推荐特性。

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 互联网技术 微信公众号

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

Elasticsearch搜索调优权威指南 (1/3)的更多相关文章

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

    本文首发于 vivo互联网技术 微信公众号 https://mp.weixin.qq.com/s/AAkVdzmkgdBisuQZldsnvg 英文原文:https://qbox.io/blog/el ...

  2. Elasticsearch搜索调优

    最近把搜索后端从AWS cloudsearch迁到了AWS ES和自建ES集群.测试发现search latency高于之前的benchmark,可见模拟数据远不如真实数据来的实在.这次在产线的bac ...

  3. Linux系统调优权威指南

    1.关闭SELINUX功能1.1 修改配置文件,使关闭SELINUX永久生效sed 's#SELINUX=enforcing#SELINUX=disables#g' /etc/selinux/conf ...

  4. elasticsearch性能调优

    转载 http://www.cnblogs.com/hseagle/p/6015245.html 该es调优版本可能有低,但是思想主体不变,不合适的参数可以自己找最新的版本相应的替代,或者增删 ela ...

  5. XGBoost参数调优完全指南(附Python代码)

    XGBoost参数调优完全指南(附Python代码):http://www.2cto.com/kf/201607/528771.html https://www.zhihu.com/question/ ...

  6. 【转】XGBoost参数调优完全指南(附Python代码)

    xgboost入门非常经典的材料,虽然读起来比较吃力,但是会有很大的帮助: 英文原文链接:https://www.analyticsvidhya.com/blog/2016/03/complete-g ...

  7. elasticsearch 性能调优

    所有的修改都可以在elasticsearch.yml里面修改,也可以通过api来修改.推荐用api比较灵活 1.不同分片之间的数据同步是一个很大的花费,默认是1s同步,如果我们不要求实时性,我们可以执 ...

  8. XGBoost参数调优完全指南

    简介 如果你的预测模型表现得有些不尽如人意,那就用XGBoost吧.XGBoost算法现在已经成为很多数据工程师的重要武器.它是一种十分精致的算法,可以处理各种不规则的数据.构造一个使用XGBoost ...

  9. 腾讯健康码16亿亮码背后的Elasticsearch系统调优实践【>>戳文章免费体验Elasticsearch服务30天】

    [活动]Elasticsearch Service免费体验馆>>Elasticsearch Service新用户特惠狂欢低至4折>>Elasticsearch Service企 ...

随机推荐

  1. rpmrebuild 下载安装

    下载 https://jaist.dl.sourceforge.net/project/rpmrebuild/rpmrebuild/2.14/rpmrebuild-2.14.tar.gz 安装 将其做 ...

  2. hive引擎的选择:tez和spark

    背景 mr引擎在hive 2中将被弃用.官方推荐使用tez或spark等引擎. 选择 tez 使用有向无环图.内存式计算. spark 可以同时作为批式和流式的处理引擎,减少学习成本. 问题& ...

  3. css文本省略号

    这里记录下如何用CSS实现单行.多行文本溢出容器的时候用省略号代替溢出部分. 单行文本溢出容器时显示省略号的CSS实现方法 /* 规定当内容溢出元素框(容器)时隐藏 */ overflow: hidd ...

  4. 用go-module作为包管理器搭建go的web服务器

    本篇博客主要介绍了如何从零开始,使用Go Module作为依赖管理,基于Gin来一步一步搭建Go的Web服务器.并使用Endless来使服务器平滑重启,使用Swagger来自动生成Api文档. 源码在 ...

  5. Visual Studio 2019 16.1 使用 .NET Core 3.0

    一.前言 早在很久之前微软便公布 .NET Core 3.0 将支持开发Winform应用程序等等新特性,现如今 .NET Core 3.0 预览版已经出来第五个预览版了,从 .NET Core 2. ...

  6. oracle学习笔记(九) SQL常用函数说明以及使用

    SQL常用函数说明以及使用 以下补充以下常用的函数,更多的请看oracle函数API文档 to_char to_char(8.58,'9.99') to_char(8.50,'9.00') to_ch ...

  7. File获取当前目录下的所有子项 listFiles()

    package seday03; import java.io.File; /** * 获取一个目录中的所有子项 * @author xingsir */public class ListFilesD ...

  8. HTTP协议中的Range和Content-Range

    " 琢磨HTTP协议的每一个细节." HTTP协议博大精深,每一个细节都应细细体会. 否则,在协议还原的过程中,你会遇到各种问题. 今天,本文中将对HTTP协议的Range和Con ...

  9. rust 高级话题

    目录 rust高级话题 前言 零大小类型ZST 动态大小类型DST 正确的安装方法 结构体 复制和移动 特征对象 引用.生命周期.所有权 生命周期 错误处理 交叉编译 智能指针 闭包 动态分派和静态分 ...

  10. redis删除策略

    redis 设置过期时间 Redis 中有个设置时间过期的功能,即对存储在 redis 数据库中的值可以设置一个过期时间.作为一个缓存数据库,这是非常实用的.如我们一般项目中的 token 或者一些登 ...