ES使用中遇到的坑

1.ES分页超过10000条报错

es 默认采用的分页方式是 from+ size 的形式,是一种逻辑上的分页,在深度分页的情况下,采用from,to方式进行分页效率会非常的低,例如以下查询

1 GET /student/_doc/_search
2 {
3   "query":{
4     "match_all": {}
5   },
6   "from":5000,
7   "size":10
8 }

ES并不是单纯的查询5000-5010这10条数据,而是在各个分片上匹配排序并得到5010条数据,协调节点拿到这些数据再进行排序等处理,然后结果集中取最后10条数据返回。随着分页深度的加深,ES的分页效率会越来越低,甚至OOM。

ES为了性能默认情况下限制了分页的深度,如果不做任何配置,ES只能查询前10000条数据,也就是 max_result_window = 10000。如果分页到10000条数据以上,ES会报错。

解决方法:

  • 调大max_result_window的值
  • 使用scroll进行分页
  • 使用search_after进行分页(官方推荐)

2.ES 返回结果total始终是10000

      1 {
      2   "took" : 48,
      3   "timed_out" : false,
      4   "_shards" : {
      5     "total" : 1,
      6     "successful" : 1,
      7     "skipped" : 0,
      8     "failed" : 0
      9   },
     10   "hits" : {
     11     "total" : {
     12       "value" : 1000,
     13       "relation" : "eq"
     14     },
     15     "max_score" : null,
     16     "hits" : [
     17       {
     18         "_index" : "bank",
     19         "_type" : "_doc",
     20         "_id" : "0",
     21         "_score" : null,
     22         "_source" : {
     23           "account_number" : 0,
     24           "balance" : 16623,
     25           "firstname" : "Bradshaw"
     34         },
     35         "sort" : [
     36           0
     37         ]
     38       },

如上是是Es一次普通查询返回的结果集

  • hits 用来实际搜索结果集
  • hits.total 是包含与搜索条件匹配的文档总数信息的对象
  • hits.total.value 表示总命中计数的值(必须在hits.total.relation上下文中解释)

要注意的是hits.total.value返回的值不一定是准确的,默认情况下,hits.total.value是不确切的命中计数,在这种情况下,当hits.total.relation的值是eq时,hits.total.value的值是准确计数。当hits.total.relation的值是gte时,hits.total.value的值是不准确的。需要设置track_total_hits为true,才能使hits.total.value的结果准确

1 GET /student/_doc/_search
2 {
    "track_total_hits":true;
3   "query":{
4     "match_all": {}
5   },
6   "size":0
7 }

当然如要统计文档数量我们更推荐使用count,而不是通过hits.total.value来表示.

参数track_total_hits可以控制total值的准确性,可以设为false、true或正整数。

  • 当为false时,不再返回total计数,total始终为1;
  • 当为正整数时,表示精确的命中文档计数,例如为1000时,此时如果命中结果小于或等于1000时,value是精确的计数,relation是eq,如果命中的结果数大于了1000时,此时value等于1000,relation是gte,如果需要精确统计结果数,这个值要设置的很大或者设为true
  • 当track_total_hits设置为true时,hits.total.value的值始终是准确的但是会影响查询性能。

3.terms分桶聚合结果不准确

ES基于分布式,每一个搜索请求都是分发到所有的分片上单独处理,最后汇总结果。聚合也是这样操作。

在 Terms Aggregation 的返回中有两个特殊的数值

  • doc_count_error_upper_bound:被遗漏的 term 分桶包含的文档有可能的最大值,也就是没有在这次聚合中返回,但是可能存在的潜在聚合的值。
  • sum_other_doc_count: 处理返回结果 bucket 的 terms 以外,其他 terms 的文档总数(总数 -返回的总数),这次聚合中没有统计到的文档数。

因为ES为分布式部署,不同文档分散于多个分片,这样当聚合时,会在每个分片上分别聚合,然后由协调节点汇总结果后返回。聚合结果会按照value从高到低进行排序。terms聚合也可以指定一个size,表示返回聚合的数量,如果设置size为3,则表示返回value排名前三的结果,这时每个分片都只聚合了本分片中排名前三的桶。其他没有统计到的文档数由每个节点返回后,汇总为此元素。sum_other_doc_count显示多少,就表示有多少个文档没有参与此次聚合。所以terms聚合并不是准确的聚合

解决

  • 当数据量大时,可以不设置分片,所有数据在一个分片上
  • 聚合时调大shard_size 参 数,降低 doc_count_error_upper_bound 来提升准确度默认shard_size = size *1.5 +10
  • size调大,提高聚合结果的数量,可以保证结果近似准确。

4.ES时间类型的查询和存储问题

在Elasticsearch内部,不论 date 是什么展示格式,所有date类型数据(时间字符串 or 时间戳等)在 Elasticsearch 内部存储时全部都会转换成 UTC时间戳(并且把时区也会计算进去),最后以milliseconds-since-the-epoch 作为存储的格式。date类型字段上的查询会在内部被转为对long型值的范围查询,查询的结果类型是字符串。

  • GMT:格林威治标准时间
  • UTC:世界协调时间
  • DST:夏日节约时间
  • CST:中国标准时间

具体细节参照博客https://blog.csdn.net/lijingjingchn/article/details/106925871

我们的项目中为了避免时区问题用到了两种方案

  • ES中Data类型的数据指定格式化方式,数据存入ES使用 字符串的日期时间,查询ES也直接使用字符串的日期时间

    PUT  test-date
    {
      "mappings": {
        "time": {
          "properties": {
            "date": {
              "type": "date",
              "format": "yyyy-MM-dd HH:mm:ss"
            }
          }
        }
      }
    }
    
    POST test-date/time/1
    {
        "date":"2017-09-15 14:50:16"
    }
    
    
    GET test-date/time/_search
    {
      "query":{
        "term": {
          "date": {
            "value": "2017-09-15 14:50:16"
          }
        }
      }
    }
    
  • ES的日期时间使用long类型的时间戳存储,日期时间类型的数据存入ES先用Java将其转为时间戳再存入ES。同样查询时也将日期时间转为时间戳再进行查询

posted @ 2021-12-22 09:46  下海搬砖  阅读(756)  评论(0编辑  收藏  举报