接触ES后遇到的一些问题

1|0群里的问题

1|1hive表数据量19亿左右(id有少量重复),把id作为es的_id进行写入,发现es中的数据量比hive通过id去重后的数据要多几万条

force merge之后做一次count试试,你看到的数据条数可能包含同一条数据不同版本的。

2|0es小知识点

2|1动态映射

都知道动态映射会创建mapping模板,but如果你在创建索引时手动指定某个字段的mapping后,动态映射还会再创建一次,且自动驼峰转下划线,比如

"brand_id" : { //比如手动设置为brandId,结果创建后都有 "type" : "long" }, "brandId" : { "type" : "keyword" }

所以最好直接写入转换下划线后的数据

2|2有助于理解es组合查询

https://cloud.tencent.com/developer/article/1689238

2|3商城筛选商品且过滤参数的DSL

实现全文搜索和筛选

GET /es_idx_item/_search/ { "from": 0, "size": 20, "query": { "bool": { "must": [ { "match": { "name_en": "Shoes" } } ], "filter": [ { "bool": { "must":[ {"term":{"category_id":"7038"}}, {"term":{"brand_id":"123534847"}} ] } } ] } } }

2|4CreateIndexRequest和IndexRequest有什么区别?

前者是用来创建并配置索引的,后者是将数据与索引相关联,并且让数据可以被搜索。

2|5如何确定git分支是从哪个分支拉下来的?

git reflog show branch_name

2|6如果是多语言搜索,该如何设置查询条件?

ES7.15中,混合语言搜索自动分词,且不需要在mapping中设置分词器,应该是服务端自动匹配分词了。

2|7Logstatsh 指定启动配置文件

bin/logstash -f conf/conf_file

https://doc.yonyoucloud.com/doc/logstash-best-practice-cn/get_start/hello_world.html

2|8fastjson序列化转换

// SnakeCase 转为 _ // CamelCase 转为驼峰 // KebabCase 转为 - // PascalCase 转为 单词首字母大写

https://github.com/alibaba/fastjson/wiki/PropertyNamingStrategy_cn

2|9ES困惑

检索类型很多,不知道选择哪一个? 嵌套查询,不知道什么时候 bool 组合,什么时候单个查询?

其一:全文检索,应用场景是:全文检索场景,比如:大数据系统。举例:检索包含:“牙膏 黑人”的内容。 核心使用的类型包含但不限于: match、match_phrase、query_string 等检索类型。

其二:精准匹配。应用场景是:精确匹配的场景,比如:搜索邮编、电话号码等信息的精准查询。 包含但不限于:terms、term、exists、range、wildcard 等。

如上单类检索解决不了组合的问题, 比如:期望检索:正文内容=XXX, 标题=YYY,发布时间介于XXX到XXX,且发文来源是:新华网的信息, 这时候就得组合检索,组合检索就需要使用 bool 组合检索。 引申出 bool 组合检索的语法,包含但不限于: must、should、must_not、filter、minum_should_count 等的组合。

2|10!!!!!ES bulkprocessor数据同步

  • 排除环境问题,可能是mapping的字段类型和source的数据字段类型不一致导致的

  • 搜索、更新和新建doc可以用索引别名,创建索引不能用别名,只能指定索引名,然后关联别名

    • public void processorUpdateItem(List<Item> itemList) { final List<ItemIndexTemplate> templates = itemList.stream() .parallel() .map(templateConvert::convert).collect(Collectors.toList()); templates.forEach(temp -> { final String source = JSONObject.toJSONString(temp, config); final IndexRequest indexRequest = new IndexRequest(itemIndexAlias) .id(String.valueOf(temp.getId())) .source(source, XContentType.JSON); processor.add(indexRequest); }); }

Elasticsearch exception [type=illegal_argument_exception, reason=no write index is defined for alias [item_index_alias]. The write index may be explicitly disabled using is_write_index=false or the alias points to multiple indices without one being designated as a write index]"

这种情况需要指定写索引,当一个别名关联多个索引,写入时不指定写入到哪个索引中会报以上错误

2|11ES 数据建模

  • 相同或相近含义字段,一定要统一字段名、字段类型

  • 特定字段独立建模

  • 索引生命周期管理方案

    • 6.6+
      • ILM
    • 7.9+
      • data stream
  • 分片设置多少?

    • 索引创建后不可修改
    • 考虑数据量、数据节点规模
  • refresh_interval

    • 数据由index buffer的堆内存缓存区刷新到堆外内存区域,形成segment
  • 分页方式

    • search_after
      • 一页一页翻
    • scroll
      • 全量导出
  • 管道预处理ingest

    • 比如像logstash的filter阶段

2|12别名对应多个索引时

  • es会自动查询所有索引的分片

  • 当多个索引指向同一个别名时,这些索引各自都是一个分片(默认1分片1副本),检索时都会检索到

2|13sort的mode默认是什么?

没有默认值,所谓的mode是ScoreMode,是枚举

2|14今天又是被nested折磨的一天

如何对nested中的某个对象更新?

伪命题,直接对整个文档覆盖更新

2|15创建文档时可以使用别名创建,如果别名在创建索引时绑定过了。仅针对一个索引,多个索引时要对写入索引指定 is_write_index=true

2|16要学的东西还有很多

pri 所有的分片数

rep 所有副本数

docs.count 所有文档数

docs.deleted 所有已删除文档数

store.size 所有分片的总存储大小,包括分片的副本

pri.store.size 所有分片的大小

终于找到了佐证

https://www.elastic.co/guide/en/elasticsearch/reference/7.6/ilm-rollover.html#ilm-rollover

To see the current index size, use the _cat indices API. The pri.store.size value shows the combined size of all primary shards.

2|17spring构造方法注入导致循环依赖

用autowire

2|18spring所有bean实例化后执行指定动作

从db读数据并整理写入es,用 SmartInitializingSingleton

处理第三方比如缓存之类的,可以用@PostConstruct

2|19nested字段的查询以及对nested父级字段的查询

GET item_index_2021_12_10_102614/_search { "from": 0, "size": 20, "query": { "bool": { "should": [ { "match": { "spu_name_thai": { "query": "fruit" } } }, { "match": { "spu_name_en": { "query": "fruit" } } }, { "match": { "brand_name_thai": { "query": "fruit" } } }, { "match": { "brand_name_en": { "query": "fruit" } } } ], "filter": [ { "nested": { "query": { "bool": { "filter": [ { "term": { "skus.active": { "value": true } } }, { "term": { "skus.visible": { "value": true } } } ], "adjust_pure_negative": true } }, "path": "skus", "ignore_unmapped": false, "score_mode": "none", "boost": 1 } }, { "term": { "is_gift": { "value": false } } }, { "term": { "visible": { "value": true } } }, { "term": { "active": { "value": true } } } ] } } }

2|20查询nested文档中的指定字段

POST /item_index2021-12-03-182031/_search { "from": 0, "size": 20, "query": { "bool": { "nested": { "query": { "term": { "skus.id": { "value": "382620383", "boost": 1 } } }, "path": "skus", "ignore_unmapped": false, "score_mode": "none", "boost": 1 } } } }

2|21如何让程序更懂用户想要什么?

2|22ES from从0开始

2|23ES实战举例

1|0如何计算出某个品牌下最流行的衣服?(聚合+过滤)

使用聚合搜索

PUT /shirts/_doc/1?refresh { "brand": "gucci", "color": "red", "model": "slim" } GET /shirts/_search { "query": { "bool": { "filter": [ { "term": { "color": "red" }}, { "term": { "brand": "gucci" }} ] } }, "aggs": { "models": { "terms": { "field": "model" } } } }

2|24后置过滤器post_filter可以过滤hits中的结果

比如只让hits保留红色

{ "query": { "bool": { "filter": [ { "term": { "brand": "gucci" } } ] } }, "aggs": { "colors": { "terms": { "field": "color" } }, "colors_red": { "filter": { "term": { "color": "red" } }, "aggs": { "models": { "terms": { "field": "model" } } } } }, "post_filter": { "term": { "color": "red" } } }

2|25重新算分(rescore)可以提高搜索精确度

​ 方法是重新排序post_filter中过滤后的数据,重新算分由各分片独立执行,最终由执行查询请求的节点重新对结果排序。
​ 若rescore请求被安排了业务字段的排序(_score不算),则会抛异常。

​ 建议使用固定的“步长”分页查询。

​ 默认情况下,原始查询的得分会与rescore查询的得分组合以得到最终算分_score,可分别通过query_weightrescore_query_weight控制权重。

​ 重新算分请求可以同时有多个。多个rescore请求,最终只会有1个rescore会和原始查询组合算分。后一个rescore可以看到前一个rescore的分数, 并据此排序

2|26创建模版时需指定analyzer和search_analyzer

使搜索词被准确分词

2|27为什么最匹配的结果没有得分最高?

2|28list对象去重用stream treeset 序

final ArrayList<Integer> list = Stream.of(3, 3, 6, 3, 2, 4, 5, 6, 9).collect(Collectors.collectingAndThen(Collectors.toCollection(TreeSet::new), ArrayList::new));

去重并升序排序

public static void main(String[] args) { final ArrayList<Integer> list = Stream.of(3, 3, 6, 3, 2, 4, 5, 6, 9).collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparingInt(x -> (int) x).reversed())), ArrayList::new)); System.out.println(list); }

2|29docker找安装的软件的目录

docker exec -it elasticsearch /bin/bash

2|30es的分页参数

es的分页参数from是当前行数,不是当前页数

比如第一页是 0,10,第二页就是 10,10,第三页就是20,10

2|31analyzer icu_analyzer has not been configured in mappings

本想连到测试环境(已安装icu)下,结果连到了local(没有icu)

2|32nested字段排序

一定要指定nested_path

2|33Unrecognized SSL message, plaintext connection?

在使用 https 协议访问网络资源时无法识别 SSL 信息,不用https就好了,或者用https但是肯定要配置证书

2|34冻结索引

POST sale_index_test_bulk/_freeze POST sale_index_test_bulk/_unfreeze

冻结的索引,不可写入查询,解冻后即可写入查询。

这在做搜索降级时用来测试搜索功能很方便

2|35向量搜索(淘宝拍图搜同款)

阿里knn写入性能不好,占内存高,京东vearch,scann/faiss[是个库,得自己包服务],hnsw

2|36如何指定写索引(别名切换写入索引)

在创建索引并绑定别名时指定is_write_index=true,默认false,同一个别名下只允许一个索引指定is_write_index=true

#为索引创建别名 POST /_aliases { "actions": [ { "add": { "index": "test_index", // 原写入索引 "alias": "my_alias", "is_write_index":false } }, { "add": { "index": "test_index01", "alias": "my_alias", "is_write_index":true } } ] }

2|37如何做数据迁移

1|0创建新索引,设置修改后的mapping

1|0迁移数据

POST _reindex { "source": { "index": "my-index-000001" }, "dest": { "index": "my-new-index-000001", "op_type": "create" // 仅当dest索引不存在这个doc才会创建 } }

1|0迁移完成后别名切换写入索引

POST _aliases { "actions": [ { "add": { "index": "my-index-000002", "alias": "my-index-alias", "is_write_index": false // 最好写上 } }, { "add": { "index": "my-new-index-000002", "alias": "my-index-alias", "is_write_index": true //must } } ] }

1|0Warn: 迁移前确保数据没有修改中的状态

1|0Warn: 迁移完成后需将原索引 _freeze,防止数据混乱

1|0如何处理数据迁移过程中可能存在的数据遗失问题?

1|0补偿机制

比如,从迁移开始,单独记录一个索引(Or 缓存,预估用时设置失效时间) X,其中记录itemId,代表要重算的数据

当迁移完成,做别名切换,关闭单独记录的itemId,再读取数据重新写入es

2|38Rollover滚动索引实践

PUT %3Cmy-index-%7Bnow%2Fd%7D-000001%3E { "aliases": { "my-alias1": { "is_write_index": true } } } # 2、批量导入数据 PUT my-alias1/_bulk {"index":{"_id":1}} {"title":"testing 01"} {"index":{"_id":2}} {"title":"testing 02"} {"index":{"_id":3}} {"title":"testing 03"} {"index":{"_id":4}} {"title":"testing 04"} {"index":{"_id":5}} {"title":"testing 05"} # 3、rollover 滚动索引 POST my-alias1/_rollover { "conditions": { "max_age": "7d", "max_docs": 5, "max_primary_shard_size": "50gb" } } GET my-alias1/_count # 4、在满足滚动条件的前提下滚动索引 PUT my-alias1/_bulk {"index":{"_id":6}} {"title":"testing 06"} # 5、检索数据,验证滚动是否生效 GET my-alias1/_search GET my-index-2021.12.28-000001

2|39es中如何把特殊字符替换为空格

GET /_analyze { "tokenizer": "keyword", "char_filter": [ { "type": "mapping", "mappings": [ "\\u005c=>\\u0020" ] } ], "text": "My license plate is/" }

本质是Native和ASCII的转换

1|0特殊字符映射整理

"char_filter": { "my_en_char_filter": { "type": "mapping", "mappings": [ ".=>\\u0020", "/=>\\u0020", "#=>\\u0020", "(=>\\u0020", ")=>\\u0020", "_=>\\u0020", "==>\\u0020", "+=>\\u0020", "&=>\\u0020", "!=>\\u0020", "@=>\\u0020", "$=>\\u0020", "%=>\\u0020", "^=>\\u0020", "*=>\\u0020", "?=>\\u0020", ",=>\\u0020", "'=>\\u0020", "\"=>\\u0020", "[=>\\u0020", "]=>\\u0020", "{=>\\u0020", "}=>\\u0020", ":=>\\u0020", ";=>\\u0020", "\\u005c=>\\u0020", "|=>\\u0020" ] } }

2|40使用java代码发送异步的Reindex请求并处理返回结果

public Response<String> reindexAsync(String src, String dest) throws IOException { final ReindexRequest reindexRequest = new ReindexRequest().setSourceIndices(src).setDestIndex(dest).setRefresh(true); final Request request = RequestConverters.reindex(reindexRequest); final org.elasticsearch.client.Response response = lowerClient.performRequest(request); final InputStream inputStream = response.getEntity().getContent(); if (inputStream != null) { final InputStreamReader inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8); final Gson gson = new Gson(); final ReindexAsyncResponse asyncResponse = gson.fromJson(inputStreamReader, ReindexAsyncResponse.class); return Response.success(asyncResponse.getTask()); } return Response.errorMsg(null, "cannot fetch response"); }
@Data public class ReindexAsyncResponse { private String task; }

2|41es如何删除已完成的异步Task,以节省space?

有网友疑惑同我,在官方社区提问了...
https://discuss.elastic.co/t/how-to-delete-a-task-in-elasticsearch-v7-14/283321

最后说是用

DELETE .tasks/task/_doc/

"This is simply a document DELETE."

然而,我实际测试后发现,任务完成后task就自动移除了,我的版本是7.15

2|42elasticsearch如何确定哪个索引的 is_write_index=true ?

GET test_item_index_222_alias/_alias { "test_item_index_666" : { "aliases" : { "test_item_index_222_alias" : { "is_write_index" : true } } }, "test_item_index_222" : { "aliases" : { "test_item_index_222_alias" : { "is_write_index" : false } } } } // java 代码里可能拿到返回数据后要遍历判断了

__EOF__

本文作者夜旦
本文链接https://www.cnblogs.com/woooodlin/p/15822774.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   夜旦  阅读(2163)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下
点击右上角即可分享
微信分享提示