优化Elasticsearch查询性能

给文件系统缓存提供内存

  Elasticsearch严重依赖于文件系统缓存,以便快速进行搜索。 通常应该确保至少有一半的可用内存进入文件系统缓存,以便Elasticsearch可以将索引的热区域保留在物理内存中。

使用更快的硬件

  如果搜索受I / O限制,应该调查为文件系统缓存提供更多内存(参见上文)或购买更快的驱动器。 特别是已知SSD驱动器比旋转磁盘性能更好。 始终使用本地存储,应避免使用NFS或SMB等远程文件系统。 还要注意虚拟化存储,例如亚马逊的Elastic Block Storage。 虚拟化存储与Elasticsearch配合得非常好,因为设置起来非常快速和简单,所以它很有吸引力,但与专用本地存储相比,它在本质上也很慢。 如果在EBS上放置索引,请务必使用预配置IOPS,否则可能会快速限制操作。
  如果搜索受CPU限制,您应该调查购买更快的CPU。

文档建数据模板

  应对文档进行建模,以使搜索时间操作尽可能便宜。
  特别是,应该避免连接。 嵌套(nested )可以使查询慢几倍,父-子(parent-child)关系的数据可以使查询慢几百倍。 因此,如果可以通过非规范化文档来回答相同的问题,则可以预期显着的加速。(强烈建议,因为我也是自己自定义模板的,后面我会出一篇专门说如何设计数据模板的文章)

搜索尽可能少的字段

  query_stringmulti_match查询所针对的字段越多,ES就越慢。 提高多个字段的搜索速度的常用技术是在索引时将其值复制到单个字段中,然后在搜索时使用此字段。 这可以使用映射的copy-to指令自动执行,而无需更改文档源。
  下面是一个包含电影的索引示例,这些电影通过将两个值都索引到name_and_plot字段中来优化搜索电影名称和图表的查询。

PUT movies
{
  "mappings": {
    "_doc": {
      "properties": {
        "name_and_plot": {
          "type": "text"
        },
        "name": {
          "type": "text",
          "copy_to": "name_and_plot"
        },
        "plot": {
          "type": "text",
          "copy_to": "name_and_plot"
        }
      }
    }
  }
}

预索引数据

  应该利用查询中的模式来优化数据索引的方式。
  例如,如果所有文档都有一个price字段,并且大多数查询在固定的范围列表上运行范围聚合,则可以通过将范围预先索引到索引中并使用术语聚合来加快此聚合。 例:创建一条文档:

PUT index/_doc/1
{
  "designation": "spoon",
  "price": 13
}

搜索请求如下

GET index/_search
{
  "aggs": {
    "price_ranges": {
      "range": {
        "field": "price",
        "ranges": [
          { "to": 10 },
          { "from": 10, "to": 100 },
          { "from": 100 }
        ]
      }
    }
  }
}

然后可以通过索引时间范围的price_range字段来压缩文档,该字段应该映射为keyword

PUT index
{
  "mappings": {
    "_doc": {
      "properties": {
        "price_range": {
          "type": "keyword"
        }
      }
    }
  }
}

PUT index/_doc/1
{
  "designation": "spoon",
  "price": 13,
  "price_range": "10-100"
}

然后搜索请求可以聚合此新字段,而不是在price字段上运行范围聚合。

GET index/_search
{
  "aggs": {
    "price_ranges": {
      "terms": {
        "field": "price_range"
      }
    }
  }
}

Mappings

  某些数据虽然是数字并不意味着会映射为数字字段。通常,存储标识符的字段(例如ISBN或从另一个数据库中识别记录的任何数字)可以将其映射为keyword而不是integerlong

避免脚本

  一般来说,应该避免使用脚本。但如果是必要的,应该使用painless或者expressions脚本引擎。

搜索舍入日期

  使用now的日期字段上的查询通常不能缓存,因为被匹配的范围一直在变化。但是,从用户体验的角度来看,切换到一个完整的日期通常是可以接受的,并且可以更好地利用查询缓存。
  例如下面的查询:

PUT index/_doc/1
{
  "my_date": "2016-05-11T16:30:55.328Z"
}

GET index/_search
{
  "query": {
    "constant_score": {
      "filter": {
        "range": {
          "my_date": {
            "gte": "now-1h",
            "lte": "now"
          }
        }
      }
    }
  }
}

可以用以下查询替换:

GET index/_search
{
  "query": {
    "constant_score": {
      "filter": {
        "range": {
          "my_date": {
            "gte": "now-1h/m",
            "lte": "now/m"
          }
        }
      }
    }
  }
}

  在这种情况下,我们舍入到分钟,因此如果当前时间是16:31:29,范围查询将匹配my_date字段的值在15:31:0016:31:59之间的所有内容。 如果多个用户在同一分钟内运行包含此范围的查询,则查询缓存可以帮助加快速度。 用于舍入的间隔越长,查询缓存可以提供的帮助越多,但要注意过于激进的舍入也可能会损害用户体验。
注意:为了能够利用查询缓存,可能很容易将范围分割成一个大的可缓存部分和一个小的不可缓存部分,如下所示:

GET index/_search
{
  "query": {
    "constant_score": {
      "filter": {
        "bool": {
          "should": [
            {
              "range": {
                "my_date": {
                  "gte": "now-1h",
                  "lte": "now-1h/m"
                }
              }
            },
            {
              "range": {
                "my_date": {
                  "gt": "now-1h/m",
                  "lt": "now/m"
                }
              }
            },
            {
              "range": {
                "my_date": {
                  "gte": "now/m",
                  "lte": "now"
                }
              }
            }
          ]
        }
      }
    }
  }
}

但是,这种做法可能会使查询在某些情况下运行得更慢,因为bool查询引入的开销可能会破坏更好地利用查询缓存的节省。

Force-merge只读索引

  只读索引将从合并到单个段中获益。基于时间的索引通常是这样的:只有当前时间框架的索引获得新文档,而旧索引是只读的。
注意:不要强制将合并索引写入到后台合并过程中。

全局序数预加载

  全局序数是一个数据结构,用于在keyword 字段上运行术语聚合。它们被延迟地加载到内存中,因为ES不知道哪些字段将用于术语聚合,哪些字段不会。可以通过mapping映射(如下)在刷新时告诉Elasticsearch加载全局序数:

PUT index
{
  "mappings": {
    "_doc": {
      "properties": {
        "foo": {
          "type": "keyword",
          "eager_global_ordinals": true
        }
      }
    }
  }
}

文件系统缓存预加载

  如果重新启动运行Elasticsearch的计算机,则文件系统缓存将为空,因此在操作系统将索引的热区域加载到内存之前需要一些时间,以便搜索操作很快。 根据使用index.store.preload设置的文件扩展名,可以显式地告诉操作系统应该将哪些文件加载到内存中。预加载设置。
注意:如果文件系统缓存不够大,无法保存所有数据,那么在太多索引或太多文件上急切地将数据加载到文件系统缓存中会使搜索速度变慢。谨慎使用。

将标识符映射为keyword

  如果文档中有数字标识符,则很容易将它们映射为数字,这与它们的json类型一致.但是,Elasticsearch索引数字的方式会优化范围查询,而keyword字段在术语查询时更好。 由于标识符从未在范围查询中使用,因此应将它们映射为关键字。

使用索引排序来加速连接

  索引排序可以很有用,以便以稍微慢点的索引为代价使连接更快。在索引分类文档中阅读更多信息

使用preference来优化缓存利用率

  有多个缓存可以帮助搜索性能,例如文件系统缓存请求缓存查询缓存。然而,所有这些缓存都是在节点级别上维护的,这意味着,如果您在一行中运行相同的请求两次,拥有一个或多个副本,并使用默认的路由算法round robin,那么这两个请求将进入不同的碎片副本,从而节点级缓存发挥不起作用。
  由于搜索应用程序的用户通常会一个接一个地运行类似的请求,例如为了分析较窄索引子集,使用标识当前用户或会话的偏好值可以帮助优化高速缓存的使用。

副本可能有助于提高吞吐量,但并非总是如此

  除了提高弹性外,副本还可以帮助提高吞吐量。例如,如果您有单个分片索引和三个节点,则需要将副本数设置为2,以便总共拥有3个分片副本,以便使用所有节点。
  现在假设你有一个2分片索引和两个节点。在一种情况下,副本的数量是0,这意味着每个节点拥有一个分片。在第二种情况下,副本的数量是1,这意味着每个节点具有两个分片。哪种设置在搜索性能方面表现最佳?通常,每个节点总共具有较少分片的设置将表现得更好。原因是它为每个分片提供了更大的可用文件系统缓存份额,而文件系统缓存可能是Elasticsearch的第一性能因子。同时,请注意,在单节点发生故障的情况下,没有副本的设置会出现故障,因此在吞吐量和可用性之间需要进行权衡。
  那么正确的副本数据是多少?如果您的群集具有num_nodes节点,则总共有num_primaries主分片,如果您希望最多能够同时处理max_failures节点故障,那么适合您的副本数量为max(max_failures,ceil(num_nodes /) num_primaries) - 1)

打开自适应副本选择

  当存在多个数据副本时,elasticsearch可以使用一组称为adaptive replica selection的标准,根据包含每个碎片副本的节点的响应时间、服务时间和队列大小,选择数据的最佳副本。这可以提高查询吞吐量并减少搜索量大的应用程序的延迟。

猜你喜欢

转载自blog.csdn.net/aa1215018028/article/details/84142480