ElasticsearchStatusException

1,874 阅读3分钟

这是我参与8月更文挑战的第1天,活动详情查看:8月更文挑战

记录一次ES线上业务使用异常的处理过程

业务场景

线上业务做视频推荐,采用es的打分函数,进行动态排序推荐,具体实现可学习大佬 程序_艺术_人生的文章,十分全面:juejin.cn/post/698026…

错误日志

  • error message:{"@type":"org.elasticsearch.ElasticsearchStatusException","detailedMessage":"org.elasticsearch.ElasticsearchStatusException: Elasticsearch exception [type=search_phase_execution_exception, reason=all shards failed]","fragment":true,"headerKeys":[],"localizedMessage":"Elasticsearch exception [type=search_phase_execution_exception, reason=all shards failed]","message":"Elasticsearch exception [type=search_phase_execution_exception, reason=all shards failed]","metadataKeys":["es.phase"]
  • stackTrace:[{"className":"org.elasticsearch.rest.BytesRestResponse","fileName":"BytesRestResponse.java","lineNumber":177,"methodName":"errorFromXContent","nativeMethod":false},{"className":"org.elasticsearch.client.RestHighLevelClient","fileName":"RestHighLevelClient.java","lineNumber":1793,"methodName":"parseEntity","nativeMethod":false},{"className":"org.elasticsearch.client.RestHighLevelClient","fileName":"RestHighLevelClient.java","lineNumber":1770,"methodName":"parseResponseException","nativeMethod":false},{"className":"org.elasticsearch.client.RestHighLevelClient","fileName":"RestHighLevelClient.java","lineNumber":1527,"methodName":"internalPerformRequest","nativeMethod":false},{"className":"org.elasticsearch.client.RestHighLevelClient","fileName":"RestHighLevelClient.java","lineNumber":1484,"methodName":"performRequest","nativeMethod":false},{"className":"org.elasticsearch.client.RestHighLevelClient","fileName":"RestHighLevelClient.java","lineNumber":1454,"methodName":"performRequestAndParseEntity","nativeMethod":false},{"className":"org.elasticsearch.client.RestHighLevelClient","fileName":"RestHighLevelClient.java","lineNumber":970,"methodName":"search","nativeMethod":false}

问题定位

1、关键日志错误定位:

  • type=search_phase_execution_exception, reason=all shards failed
  • "className":"org.elasticsearch.rest.BytesRestResponse","fileName":"BytesRestResponse.java","lineNumber":177,"methodName":"errorFromXContent","nativeMethod":false 2、百度查找解决方案:
  • 得到的答案基本都是这种情况导致:当使用到term 查询的时候,由于是精准匹配,所以查询的关键字在es上的类型,必须是keyword而不能是text
  • 通过百度的答案,结合实际的项目分析,我们业务表中使用的字段可能造成的原因只有一个字段属于text类型,但是在我们使用的es版本,以及我们压测上线这段时间都没有因为这个问题导致报错,所以可以是由于排除这个问题导致的,需要别的方式确定问题

解决过程

1、模拟真实环境

  • 从日志中获取到请求参数,本地模拟线上请求,请求测试环境中相同的es数据,达到真实环境的模拟

2、获取查询dsl

  • SearchSourceBuilder,最终的查询器结果输出
logger.info(searchSourceBuilder.toString());
  • 通过日志输出,获取到es的dsl查询语句
{
    "size":10,
    "query":{
        "function_score":{
            "query":{
                "bool":{
                    "filter":[
                      {
                            "term":{
                                "status":{
                                    "value":1,
                                    "boost":1
                                }
                            }
                        }
                    ],
                    "adjust_pure_negative":true,
                    "boost":1
                }
            },
            "functions":[
                {
                    "filter":{
                        "match_all":{
                            "boost":1
                        }
                    },
                    "field_value_factor":{
                        "field":"new_test",
                        "factor":1,
                        "modifier":"none"
                    }
                }
            ],
            "score_mode":"sum",
            "boost_mode":"sum",
            "max_boost":340282350000000000000000000000000000000,
            "min_score":70,
            "boost":1
        }
    },
    "_source":{
        "includes":[
            "xxxx"
        ],
        "excludes":[

        ]
    },
    "sort":[
        {
            "_score":{
                "order":"asc"
            }
        },
        {
            "xxxx":{
                "order":"asc"
            }
        }
    ]
}

3、执行dsl语句

  • 在kibana中执行dsl语句,可以获取到查询报错信息,这里的错误信息就十分明确,functions执行中使用到了new_test字段,但是由于表中部分数据缺失这个字段信息,无法执行函数计算,就会报出这个错误异常
  • 确定问题通过排查,确定问题产生的原因有两点:
    1. 使用es的functions时,如果是使用script_score的script脚本则可以进行字段是否为空判断,但是这样会导致es的执行效率十分差,所以我们实际项目中使用了es自带的function去实现打分处理,无法进行字段是否为空的判断,所以在分数计算时若指定字段不存在就会报错

    2. 虽然使用filter可以把不包含指定字段的数据过滤掉,但是业务方实时调整,我们会把数据实时更新,难免会出现状态调整导致无效数据成为有效数据,但是又没有包含指定字段,就会导致异常,这一点也是导致我们报错的原因

"root_cause": [
      {
        "type": "exception",
        "reason": "Unable to find a field mapper for field [new_test]. No 'missing' value defined."
      }
    ],
    "type": "search_phase_execution_exception",
    "reason": "all shards failed",
    "phase": "query",
    "grouped": true

4、问题修复

  • 通过kibana 更新未包含指定字段的数据,增加默认值

POST /test/_doc/_update_by_query
{
    "query": {
        "bool" : {
           "must_not" : {
              "term" : {
                "field" : "balance"
              }
           }
         }
    },
    "script": {
        "source": "ctx._source['balance'] = 70.0"
    }
}
  • 调整实时流方案,同时增加insert和update的处理,确保数据一定存在默认值