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 } ] }
在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", 这个值默认一万,我们可以改成自己想要的值
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的查询性能也会下降,但分页查询的性能将会提升。
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