给你总结几个ES下最容易踩的坑

我本人接触Elasticsearch(一下简称ES)有挺长一段时间了,本文结合本身的一些项目经验,给你总结几个实际项目中比较容易踩到的坑。但愿读者可以避免犯这样的错误。html

坑一,时区问题

在咱们的项目中,索引下通常都会存在一个时间的字段,这个字段能够用来排序,或者作时间范围查询,或者聚合的场景等都会用到。数组

ES底层默认采用UTC时间格式,而中国的时间(CST)是缓存

CST=UTC+8

因此实际的项目中常常遇到查询结果和本身指望的不一致。关于时区的问题以及如何解决,我以前专门写了一篇文章,感兴趣的能够看看:app

ES系列之一文带你避开日期类型存在的坑elasticsearch

坑二,使用默认的mappings

ES自己支持咱们在写入一个索引的时候,能够不为该索引设置任何的mappings。这种状况下,ES会为索引根据写入的字段值,"推断"该字段的类型。分布式

看起来彷佛不错,可是根据个人经验,仍是建议应该明确的为本身的索引定义mappings。ide

由于有时候ES "推断"出来的结果并不必定是咱们想要的,而后给咱们带来一些头疼的问题。ui

我仍是给你举个例子:this

写入一个名为message的索引,.net

PUT /message/_doc/1
{
  "head":   "message001",
  "body": "2020-05-30"
}

而后继续再插入一条,

PUT /message/_doc/2
{
  "head":   "message002",
  "body": "this is an emergency"
}

这里就报错了,错误的内容是提示咱们,body这个字段没法被解析。产生这个问题的缘由是当咱们写入第一条文档的时候,ES "擅自作主"把body这个字段标记成日期类型了,而后写入文档2的时候不是日期字符串,因此没法解析。

能够用下面的命令看下索引的mapping,证明咱们的猜测:

GET message/_mapping

在这个示例中,咱们若是明确的定义body为text类型就不会有这样的问题了。

坑三,没有规划好分片

咱们通常都须要为索引设置分片的数量,具体设置成多少须要根据你的项目实际状况来定。

通常来讲,单个 Shard 的建议最大大小是 20G 左右,最大不要超过 50G。

单个shard过大,或者shard太小致使shard数量太多,都会影响查询的效率。

举个例子,好比你预估索引的大小是100G,这个时候分片是3~5比较好。

若是你的索引是天天增量比较大的场景,好比日志类,订单类的索引,可能你首先要把根据日期来新建不一样的索引,根据时间的数据规模选择按天,周,甚至月来建索引。而后这些索引使用相同的分片设置。

坑四,过多依赖ES聚合的结果

ES某些场景下的聚合结果是不许确的,计算的结果只是告诉你一个大概的分布状况,并非精确的。

若是你不了解这个状况,可能会在实际的项目中犯错误。

我曾经写过一篇文章,对这个坑有过详细的分析以及闭坑指南,有兴趣能够看看这篇文章:

ES系列之原来ES的聚合统计不许确啊

坑五,分桶聚合查询的内存爆炸

在分桶聚合的场景下,大多数时候对单个字段的聚合查询很是快的,若是是多个字段嵌套聚合。有可能撑爆内存,引起OOM。看下面一个例子。

假设咱们有个不少电影数据的索引,有个字段是数组,保存演员的名字。

{
  "actors" : [
    "Fred Jones",
    "Mary Jane",
    "Elizabeth Worthing"
  ]
}

而后,咱们但愿查询出演影片最多的10个演员,以及他们合做最多的5位演员,可使用下面这个聚合,

{
  "aggs" : {
    "actors" : {
      "terms" : {
         "field" : "actors",
         "size" :  10
      },
      "aggs" : {
        "costars" : {
          "terms" : {
            "field" : "actors",
            "size" :  5
          }
        }
      }
    }
  }
}

结果返回前10位演员,以及与他们合做最多的5位演员。可是就是这样一个简单的查询,可能致使OOM。

咱们能够想象下在内存中构建一个树来表示这个 嵌套terms 分桶聚合。 首先actors 聚合会构建树的第一层,每一个演员都有一个桶。

而后,第一层的每一个节点之下, costar 聚合会构建第二层,每一个联合演员一个桶,若是你学过排列组合,应该知道这实际上要构建n的平方个分桶。(n是每一个影片中演员的数量)

假如平均每部影片(文档)有 10 名演员,每部影片就会生成

10^2

100个桶。若是总共有 20000 部影片,粗率计算就会生成 2000000 个桶。这个引发内存爆炸就不奇怪了。

上面产生问题的根源在于ES对于这种嵌套聚合默认使用了深度优先规则,即先构建完整的树,再筛选符合条件的结果。

ES容许咱们使用一种广度优先的模式来进行这种场景的聚合,这种策略的工做方式有些不一样,它先执行第一层聚合, 再继续下一层聚合以前会先作修剪。

要使用广度优先,只需简单 的经过参数 collect 开启,

{
  "aggs" : {
    "actors" : {
      "terms" : {
         "field" :        "actors",
         "size" :         10,
         "collect_mode" : "breadth_first" 
      },
      "aggs" : {
        "costars" : {
          "terms" : {
            "field" : "actors",
            "size" :  5
          }
        }
      }
    }
  }
}

广度优先仅仅适用于每一个组的聚合数量远远小于当前总组数的状况下,由于广度优先会在内存中缓存裁剪后的仅仅须要缓存的每一个组的全部数据,以便于它的子聚合分组查询能够复用上级聚合的数据。

坑六,mapping包含的字段过多

咱们给索引建模时,要尽可能避免mapping中的包含的字段过多。

过多的字段一个是难以维护,当存在成千上百个字段时,很难有人真正明确每一个字段的含义。另一个致使的问题是,当咱们须要更新文档的时候,ES会在不一样的节点同步这些更新。过多的字段意味着更新变慢。

若是咱们的业务场景确实须要不少字段,应该充分利用ES的dynamic templates机制,提早定义好字段映射的规则,这样一些字段就没有必要在mapping里定义好。

不过不管如何,都应该尽可能保持你的mapping字段足够小。

总结

Elasticsearch 是一个分布式可扩展的实时搜索和分析引擎。这样的神器若是用好了让你的工做事半功倍,可是若是没用好可能又会给你带来很多的困扰。

先写这么多吧,后续若是工做中踩到新的坑再跟你们分享。


参考:

https://www.elastic.co/guide/...

相关文章
相关标签/搜索