ES查询踩两个坑

ElasticSearch偶尔查询不到数据

1.数据刷新策略

现象:每次insert之后,立刻查询es的数据是有可能查不到的,因为es从内存写到磁盘需要时间

原因:es默认每1s执行一次refresh,因此文档实时性被提高到1s,这也是es被称为近实时的原因

解决方法:写的时候指定数据刷新策略, request().setRefreshPolicy(RefreshPolicy.IMMEDIATE);

  枚举org.elasticsearch.action.support.WriteRequest.RefreshPolicy定义了三种策略:

NONE,

IMMEDIATE,

WAIT_UNTIL; 

2.查询条件中含有中文导致查询不到数据

elasticsearch 里默认的standard分词器是会将每一个中文都进行了分词的切割,

所以你直接想查一整个词,或者一整句话是无返回结果的。

解决办法:

1.查询条件字增加.keyword后缀

QueryBuilders.termQuery("filed.keyword", "查询中文内容")

2.text类型字段的分词器 也可以用 "analyzer": "standard_cjk_analyzer" 或者IK分词器 除了英文外 还支持中日韩 不敏感大小写

解释:ElasticSearch 5.0以后,string类型有重大变更,移除了string类型,string字段被拆分成两种新的数据类型: text用于全文搜索的,而keyword用于关键词搜索

参考文章:https://www.jianshu.com/p/26744eb914a8

不符合条件的数据查询出来了

当字段值为复杂数据类型(Object、Geo-Point等)的时候,ES内部实际是以扁平化的方式保存数据的:

比如:这两条数据

PUT /order/_doc/1
{
  "order_name": "xiaomi order",
  "desc": "shouji zhong de zhandouji",
  "goods_count": 3,
  "total_price": 12699,
  "goods_list": [
    {
      "name": "xiaomi PRO MAX 5G",
      "price": 4999
    },
    {
      "name": "ganghuamo",
      "price": 19
    },
    {
      "name": "shoujike",
      "price": 1999
    }
  ]
}
PUT /order/_doc/2
{
  "order_name": "Cleaning robot order",
  "desc": "shouji zhong de zhandouji",
  "goods_count": 2,
  "total_price": 12699,
  "goods_list": [
    {
      "name": "xiaomi cleaning robot order",
      "price": 1999
    },
    {
      "name": "dishwasher",
      "price": 4999
    }
  ]
}
View Code

在ES中会这么存储

{
  "order_name": "Cleaning robot order",
  "desc": "shouji zhong de zhandouji",
  "goods_count": 2,
  "total_price": 12699,
  "goods_list.name":[ "alice", "cleaning", "robot", "order", "dishwasher" ],
  "goods_list.price":[ 1999, 4999 ]
}

当我们执行查询语句

GET order/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "goods_list.name": "dishwasher"    // 条件一
          }
        },
        {
          "match": {
            "goods_list.price": 1999           // 条件二
          }
        }
      ]
    }
  }
}

会把不存在的数据查询出来(满足条件之一都会查出来)

上述问题解决办法即对复杂类型使用Nested类型。参考csdn: https://blog.csdn.net/wlei0618/article/details/126508180

执行查询时 在query外层嵌套一层查询: 如下:

GET /order/_search
{
  "query": {
    "nested": {
      "path": "goods_list", 
      "query": {
        "bool": {
          "must": [
            {
              "match": {
                "goods_list.name": "dishwasher"
              }
            },
            {
              "match": {
                "goods_list.price": 4999
              }
            }
          ]
        }
      }
    }
  }
}

注意点:当字段优化改成nested类型的时候 需要把查询语句一并改动

每次查询最大只能查10000条

通过资料的查阅,发现默认值是10000,如果要查询大于10000条,我们就需要修改es的max_result_window默认值

解决方法:

我们在创建索引的时候 设置:"index.max_result_window": "10000", 这个值默认一万,我们可以改成自己想要的值

也可以使用ES的Scroll滚动查询

Mapping和分片数不能更改

不能改的原因参考以前博客:https://www.cnblogs.com/ssskkk/p/11657465.html#_label4

我如果想更改咋办 比如数据量太多,分片不够用,或者Mapping因为业务变化增加字段

1.新建索引,重新映射Mapping

2.Reindex迁移数据

3.然后使用别名,替换用户的访问。

批量操作不回滚

说明:批量时不会因为⼀个失败⽽全部失败,⽽是继续执⾏后续操作,在返

回时按照执⾏的状态返回!

更新⽂档

更新⽂档
说明: 这种更新⽅式是先删除原始⽂档,在将更新⽂档以新的内容插⼊。
PUT /products/_doc/sjfYnXwBVVbJgt24PlVU
{
 "title":"iphon15"
}
说明: 这种⽅式可以将数据原始内容保存,并在此基础上更新。
POST /products/_doc/sjfYnXwBVVbJgt24PlVU/_update
{
 "doc" : {
 "title" : "iphon15"
 }
}

ES性能优化

1. 因为ES不能改变分片数量,所以创建索引的时候要指定好分片数量

ES 默认为一个索引创建 5 个主分片, 并分别为每个分片创建一个副本分片。

解决办法:合理的分片数量可以提高写入性能和稳定性。

  分片数可以理解为MySQL中的分库分表

  ES查询主要分为两类:单ID查询以及分页查询。

  分片数越大,集群横向扩容规模也更大,根据分片路由的单ID查询吞吐量也能大大提升,但聚合的分页查询性能则将降低;

  分片数越小,集群横向扩容规模也更小,单ID的查询性能也会下降,但分页查询的性能将会提升。

  ES的查询原理

2、避免深分页查询ES集群的分页查询支持from和size参数,

  查询的时候,每个分片必须构造一个长度为from+size的优先队列,

  然后回传到网关节点,网关节点再对这些优先队列进行排序找到正确的size个文档。

  假设在一个有6个主分片的索引中,from为10000,size为10,每个分片必须产生10010个结果,

  在网关节点中汇聚合并60060个结果,最终找到符合要求的10个文档。

  由此可见,当from足够大的时候,就算不发生OOM,也会影响到CPU和带宽等,从而影响到整个集群的性能。

  所以应该避免深分页查询,尽量不去使用。

解决办法:可以使用ES的Scroll滚动查询

  scroll 可以先查询出一些数据,然后再紧接着依次往下查询。在第一次查询的时候会有一个滚动id,

  相当于一个锚标记 ,随后再次滚动搜索会需要上一次搜索滚动id,根据这个进行下一次的搜索请求。

  每次搜索都是基于一个历史的数据快照,查询数据的期间,如果有数据变更,那么和搜索是没有关系的。

  性能会比上面说的分页性能要高很多,基本上都是毫秒级的。

  这个适合于那种类似微博下拉翻页的,不能随意跳到任何一页的场景。

具体官网文档:https://www.elastic.co/guide/cn/elasticsearch/guide/current/scroll.html

 3.导入大量数据时的优化

当数据添加到索引后并不能马上被查询到,等到索引刷新后才会被查询到。 refresh_interval 配置的刷新间隔。

当我们大批量的往Elasticsearch索引录入数据时,通常会把refresh_interval 设置为 -1,这样会加快数据导入的速度,在数据导入完成后,再将该参数设置为正数。比如:1s。

当 refresh_interval 为 -1 时,意味着不刷新索引。

refresh_interval 的默认值是 1s。

参考知乎:https://zhuanlan.zhihu.com/p/378295340

 

posted @ 2021-10-17 18:31  palapala  阅读(4904)  评论(1编辑  收藏  举报