elasticsearch 提高索引查询效率

提高索引的查询速度是一个优化的系统性能的重要角度,有哪些手段可以提高索引的查询速度呢?

文档建模:合理的文档模型

应该对文档进行合理的建模,这样可以提高搜索的效率。

禁用join关系。nested 会让查询慢几倍,parent-child 关系会让查询慢几百倍。

搜索尽可能少的字段

query_stringmulti_match 查询的字段越多,越慢。可以将多个字段的值拷贝到一个字段中,以提高多个字段的搜索速度。拷贝可以使用 copy_to 完成。例如,下面对字段 nameplot 的查询,可以通过将 nameplot 字段的值拷贝到 name_and_plot 字段中,只对 name_and_plot 字段查询。

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

提前索引数据

例如,索引中有个 price 字段,大多数查询都是发生在该字段上的 range 查询,并且范围是固定的。那就可以提前计算出文档对应的 price_range,以便后续的查询和聚合使用。

例如,文档是:

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,用来记录 price 字段对应的范围:

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

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

这样,查询时,就可以直接使用 price_range

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

这个思路,还可以拓展,比如,

  • 查询时需要使用脚本计算排序值,可以在文档写入时提前计算好
  • 不使用es的脚本生成字段,而是写入前提前计算好

这个优化的角度就是,让es少干点活,能提前搞好的,就提前搞,但是对于一些小索引可能没必要,所以还是要结合实际的场景

字段类型:选择合适的类型

并不是所有的数值类型的数据都应当映射为 numberic 类型的字段。虽然Elasticsearch 会为 range 查询优化数值类型的字段,例如 integer 和 long,但是 keyword 类型的字段在 term 或者其他 term-level 的查询时表现更好。

例如:如果有些数据的 ID 不会使用 range 查询,但会使用 term 查询。那么就可以考虑将其类型设为 keyword 来优化。

  • 如果不使用 range 查询
  • 为了快速检索,应该使用 keyword 类型,因为 term 查询中,keyword 通过比数值类型搜索的更快。

如果不确定使用哪个,可以通过 multi-field,映射出 keyword 和数值类型的字段。

避免使用脚本

如果有可能的话,避免使用脚本,包括:使用脚本排序,使用脚本聚合,脚本计算评分。

虽然脚本非常有用,但不能使用 Elasticsearch 的索引结构或相关优化。使用脚本有时会导致搜索速度变慢。

可参考:scripts-and-search-speed

日期搜索优化

对使用的日期字段的查询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:00和之间的所有内容16:31:59。如果多个用户在同一分钟内运行包含此范围的查询,则查询缓存可以帮助加快速度。用于舍入的间隔越长,查询缓存的帮助就越大,但要注意过于激进的舍入也可能会损害用户体验。

分段合并:强制合并只读索引

只读索引可以通过 _forcemerge 进行分段合并。这通常是基于时间的索引的情况:只有当前时间范围的索引正在获取新文档,而旧索引是只读的。已被强制合并为单个段的分片可以使用更简单、更有效的数据结构来执行搜索。

不要强制合并仍会写入的索引。依靠自动后台合并进程根据需要执行合并,以保持索引平稳运行。如果继续写入强制合并索引,那么性能可能会变得更糟。

使用 preference 提高缓存利用率

有多个缓存可以帮助提高搜索性能,例如 文件系统缓存请求缓存查询缓存。然而,所有这些缓存都是在节点级别维护的,这意味着如果您连续两次运行相同的请求,拥有 1 个或更多副本并使用默认路由算法round-robin,那么这两个请求将转到不同的分片副本,这显然不会使用到节点级缓存。

由于搜索应用程序的用户一个接一个地运行类似请求是很常见的,例如为了更好的使用到缓存,同一个搜索请求使用相同的 preference 标识可以提升缓存的使用率。

副本:副本可以提升吞吐量,但不总是

副本可以帮助提高吞吐量。例如,如果您有一个单分片索引和三个节点,则需要将副本数设置为 2,总共拥有 3 个分片副本,以便利用所有节点。

现在假设您有一个 2个主分片的索引和两个节点。在一种情况下,副本数为 0,这意味着每个节点都拥有一个分片。在第二种情况下,副本数为 1,这意味着每个节点都有两个分片。哪种设置在搜索性能方面表现最好?通常,每个节点总分片较少的设置会表现得更好。原因是它为每个分片提供了更大份额的可用文件系统缓存,而文件系统缓存可能是 Elasticsearch 的第一大性能因素。同时要注意,没有副本的设置在单节点故障的情况下会失败,因此需要在吞吐量和可用性之间进行权衡。

那么正确的副本数是多少?如果您有一个包含 num_nodes节点、总共num_primaries有主分片的集群,并且您希望最多能够同时处理节点故障为max_failures,那么适合您的副本数量是 max(max_failures, ceil(num_nodes / num_primaries) - 1)

总结

以上的几个可以着手优化的点,是开发者需要重点关注的,除了这些之外,还有一些是运维需要关注的,这些在官方文档中也有提及。

参考文档

官方文档:[tune-for-search-speed](

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lanicc

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值