Elasticsearch 정렬 문제에 대한 분석 복사

6102 단어 ElasticSearch
최근 온라인의es조회의 어떤 마이크로서비스 인터페이스에 이상이 발생했습니다. 다음과 같습니다.
nested: SearchParseException[No mapping found for [count] in order to sort on];
Caused by: SearchParseException[failed to parse search source 

직접적으로 이상에서 볼 수 있듯이 맵핑에 정렬 필드가 존재하지 않을 때 던진 이상이다. 정상적인 경우 만약에 어떤 색인이 존재하지 않고 이 색인을 조회한다면 우리는 색인 이름 뒤에 *를 붙여서 조회 오류를 피할 수 있다. 다음과 같다.
        SearchRequestBuilder search=client.prepareSearch("log2019-07-11*");
        search.setQuery(QueryBuilders.queryStringQuery("uid:111"));
        search.addSort("count", SortOrder.DESC);

문의한 결과, 우리의 코드에 이미 * 어댑터가 추가되었는데, 그러면 왜 이 문제가 발생했습니까?장면에 대한 분석과 정리를 통해 특정한 필드를 sort할 때 상기 이상을 보고할 수 있는데 주로 두 가지 상황에서 다음과 같다.
(1)count 필드는mapping에서 정의되지 않고 색인 이름 접두사에 * 어댑터를 추가하지 않았을 때 매번 이상을 보고합니다
(2)count 필드는mapping에 정의되어 있습니다. 앞의 정렬 방법을 사용하면 정렬이 틀릴 때도 있고 안 할 때도 있습니다.
첫 번째 상황에 대해mapping에는 정의가 존재하지 않고 플러그인 오류를 사용하지 않았으며, 이 필드의 정렬 오류를 직접 사용하는 것은 정상적입니다.
다음은 두 번째 상황을 분석한다. 왜냐하면 이 안에 확률 사건이 있기 때문이다. 즉, 대다수는 문제없지만 소수의 경우 이상을 던진다.분석log를 통해 새벽 12시가 막 지났을 때 이런 문제가 발생할 확률이 있다는 것을 발견했다. 더 많은 조사 단서를 찾기 위해 나는 운비에게 온라인 당시 서버es의log 파일을 제시했다. 단순히 마이크로서비스의 log에서 이 이상 전후, 더 많은 상하문log 출력이 있는지 관찰하기 어렵다. 이것을 찾으려면 서버es노드의log를 배치할 수밖에 없다. 로그를 받은 후에과연 상하문에서 다음과 같은 관건적인 정보를 발견했다.
[2019-07-11 00:00:05,044][INFO ][cluster.metadata         ] [my_es] [log2019-07-11] creating index, cause [auto(index api)], templates [log], shards [3]/[2], mappings [_default_, log]
[2019-07-11 00:00:05,409][INFO ][cluster.routing.allocation] [my_es] Cluster health status changed from [RED] to [YELLOW] (reason: [shards started [[log2019-07-11][0], [log2019-07-11][2], [log2019-07-11][1]] ...]).
[2019-07-11 00:00:06,232][INFO ][cluster.routing.allocation] [my_es] Cluster health status changed from [YELLOW] to [GREEN] (reason: [shards started [[log2019-07-11][1], [log2019-07-11][1]] ...]).
[2019-07-11 00:00:06,374][INFO ][cluster.metadata         ] [my_es] [log2019-07-11] update_mapping [log]
[2019-07-11 00:00:07,153][DEBUG][action.search            ] [my_es] [log2019-07-11][0], node[QlHmfObMRg2K6FUuAaXXTw], [R], v[2], s[INITIALIZING], a[id=3aenuvbUQtGrFDBBxjJvCA], unassigned_info[[reason=INDEX_CREATED], at[2019-07-10T16:00:06.574Z]]: Failed to execute [org.elasticsearch.action.search.SearchRequest@22e9ea6f] lastShard [trueRemoteTransportException[[my_es_02][192.168.10.152:9300][indices:data/read/search[phase/query]]]; nested: SearchParseException[failed to parse search source [{"size":3,"query":{"bool":{"should":[{"term":{"uid":"44733065"}}]}},"sort":[{"count":{"order":"desc"}}]}]]; nested: SearchParseException[No mapping found for [count] in order to sort on];
Caused by: SearchParseException[failed to parse search source 


이 이상을 보고하기 전에 두 줄의 관건적인 log가 있습니다.
[2019-07-11 00:00:05,409][INFO ][cluster.routing.allocation] [my_es] Cluster health status changed from [RED] to [YELLOW] (reason: [shards started [[log2019-07-11][0], [log2019-07-11][2], [log2019-07-11][1]] ...]).
[2019-07-11 00:00:06,232][INFO ][cluster.routing.allocation] [my_es] Cluster health status changed from [YELLOW] to [GREEN] (reason: [shards started [[log2019-07-11][1], [log2019-07-11][1]] ...]).


그 동안 이상을 보고하기 2초 전에 es군집 상태는 레드에서 옐로우, 그린까지 짧은 1초였지만 문제를 조사하는 데 매우 관건적인 정보였다. 우리는 이런 정보가 발생했을 때 es의 메인 디스크와 던전 디스크가 초기화되거나 이상이 발생하여 복구를 기다리고 있다는 것을 알 수 있다. 이로써 이런 장면을 추론할 수 있다. 새벽 12시가 막 지난 5초에 이 인덱스에 기록된es데이터가 있다.es동적 인덱스 생성을 촉발했습니다. 이 인덱스는 3개의shard가 있습니다. 각각shard는 각각 2개의 복사본shard가 있습니다. 즉, 모두 6개의shard가 초기화되어야 합니다. 초기화에 걸리는 시간이 2초라고 가정하면 공교롭게도 6초에 검색대 정렬이 있습니다. 이때 인덱스 초기화가 완료되지 않았기 때문에 당일 부분shard의mapping에서 이 필드의 정보를 찾지 못했습니다.그러므로 이상이 발생했습니다. 이후 이 인덱스가 생성된 후에 다시 받은 새로운 검색 요청은 문제없습니다.이 추측을 검증하기 위해서는 테스트를 위한 절차가 필요합니다.
디자인의 재현 장면은 다음과 같은 두 가지 단계가 있다.
(1) A라인 아날로그 조회 요청은 어느 날의 인덱스 데이터를 조회해 왔다. 처음에 이 인덱스는 존재하지 않았다. 존재하지 않기 때문에 조회는 어댑터를 사용하는 방법으로 정렬 필드가 있어도 틀리지 않았다.A라인의 쿼리 코드는 다음과 같습니다.
        SearchRequestBuilder search=client.prepareSearch("log2019-07-11*");
        search.setQuery(QueryBuilders.queryStringQuery("uid:111"));
        search.addSort("count", SortOrder.DESC);
        SearchResponse sr=search.get();
        for ( SearchHit hit: sr.getHits()){
            System.out.println(hit.getSource().toString());
        }
        System.out.println(Thread.currentThread().getName()+"=>query"+"   hit="+sr.getHits().getTotalHits());

(2) B 라인은 A 라인이 시작된 지 3초 후에 실행한 다음에 존재하지 않는 색인에 데이터를 삽입합니다. 첫 번째 데이터 삽입으로 인해 이날 색인은 자동으로 생성되고 그 안에shard의 초기화 과정과 관련됩니다. 이때 A 라인이 조회할 때 문제가 반복됩니다.
[No mapping found for [count] in order to sort on]

B 스레드의 쓰기 코드는 다음과 같습니다.
        IndexRequestBuilder builder = client.prepareIndex("log2019-07-11", "log").setId("111");
        builder.setCreate(true);
        java.util.Map map=new HashMap<>();
        map.put("uid","111");
        map.put("count","3");
        builder.setSource(map);
        builder.execute().actionGet();
        System.out.println(Thread.currentThread().getName()+" => " + "create index finished. " );

문제를 알게 된 후, 그러면 어떻게 이 문제를 해결합니까?사실 매우 간단하다. 우리는 정렬이 존재하지 않는 필드에 대해 부족한 용착을 하기만 하면 된다. 다음과 같다.
search.addSort("count", SortOrder.DESC);

... 로 바꾸다
search.addSort(SortBuilders.fieldSort("count").unmappedType("integer").order(SortOrder.DESC));

위의 코드는 es가 정렬할 때 존재하지 않는 필드를 만나면 정확한 정렬 형식을 설정하면 앞에서 언급한 두 가지 정렬 실패의 원인을 피할 수 있음을 알려준다.
(1)count 정렬 필드는mapping에 정의가 없습니다. 색인 이름 접두사에 * 어댑터를 추가하지 않았을 때 매번 이상을 보고합니다. (색인 이름이 있든 없든 상관없이, 필드가 존재하지 않습니다.
(2)count 정렬 필드는mapping에 정의되어 있습니다. 앞의 정렬 방법을 사용하면 정렬이 틀릴 때도 있고 안 될 때도 있습니다.
es 인덱스 자체는shemeless의 구조로 정상적으로 존재하지 않는 필드를 조회하면 오류를 보고하지 않지만 정렬된 필드는 이상이 발생할 수 있기 때문에 우리는 관련 코드를 쓸 때 정렬 필드를 용착 처리하여 우리 프로그램의 건장성을 높일 수 있습니다.

좋은 웹페이지 즐겨찾기