Elasticsearch version conflict 에러

배치를 이용해서 Elasticsearch에 데이터를 삽입하던 중 version conflict라는 오류가 자주 발생했다. 처음에는 Elasticsearch 버전이 동일한데 왜? 오류가 나는지 몰랐다.

그래서 검색해보니 인덱스안에 document에는 각자 관리하는 version이 존재한다. 이 version은 document가 수정될 때 하나씩 올라가게 되는데 version이 10인 상태에 document에 여러 서버 모듈에서 해당 document에 업데이트를 하려고 하니 문제가 발생하였다.

그 이유는 version 10인 상태에서 작업에 들어간 두 모듈은 한 모듈이 먼저 11로 업데이트를 시키고 다음 모듈이 작업을 진행하려고 할 때 자기가 알고 있던 마지막 version인 10이 아니라 11로 바껴있는것을 보고 에러를 뱉어내는것이다. 이렇게 까지 세심하게 챙겨줄지 몰랐다. 알면 알수록 elasticsearch라는 db는 정말 매력적이다.

PUT wedul_index 
{
  "mappings": {
      "_doc": {
        "dynamic": "false",
        "properties": {
          "name": {
            "type": "text"
          }
        }
      }
  }
}

위와 같이 인덱스가 있고 document 하나가 들어있다. 여기에 age라는 값과 gender를 집어넣어보자. 이를 동시에 호출해보자.

document

그럼 document 하나에 필드를 동시에 업데이트하는 update.sh라는 스크립트를 만들어서 실행시켜보자.

curl -X POST "localhost:9200/wedul_index/_update_by_query" -H 'Content-Type: application/json' -d' { "script": { "source": "ctx._source[\u0027gender\u0027] = \u0027M\u0027"}, "query": { "match": { "name": "위들" } } } ‘
curl -X POST "localhost:9200/wedul_index/_update_by_query" -H 'Content-Type: application/json' -d' { "script": { "source": "ctx._source.age = 10", "lang": "painless" }, "query": { "match": { "name": "위들" } } } ‘

그럼 위에 설명했던 것 처럼 버전이 먼저 변경이 되면서 다음과 같은 에러를 뱉어낸다.

[{"index":"wedul_index","type":"_doc","id":"3MSd5WsB_jV9Cf9TkYLV","cause":{"type":"version_conflict_engine_exception","reason":"[_doc][3MSd5WsB_jV9Cf9TkYLV]: version conflict, current version [3] is different than the one provided [2]","index_uuid":"sJI8sBnrTP-OW8OG8YBqWA","shard":"3","index":"wedul_index"},"status":409}]

 

이를 해결하기 위해서는 retry_on_conflict 옵션을 함꼐 부여할 수 있는데 이 옵션은 version conflict이 발생했을 때, 업데이트 재시도를 몇회 할건지 지정하는 옵션이다.

좀 더 자세한 사항은 아래 elasticsearch 메뉴얼을 보면 자세히 나와있다.

참조
https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-update-by-query.html
https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-update.html

댓글()

Elasticsearch reindex시 alias를 사용하여 무중단으로 진행하기 & big index 리인덱싱 시 비동기 처리 방법

Elasticsearch reindex를 진행할 때, 단순하게 새로운 인덱스를 만들고 reindex api를 진행하고 기존 인덱스를 지우고 새로 만들어서 다시 reindex를 해줬다. (이전글: https://wedul.site/611?category=680504)

하지만 그것은 해당 인덱스의 document의 수가 적어서 금방 진행이 되었었고 만약 document수가 10만가지만 넘어도 생각보다 오래걸려서 서비스의 흐름이 끊어지게 된다는걸 인지하지 못했다. 같은 회사 동료분께서 해당 부분에 대해서 말씀해주셨고, 그 분이 가이드 해주신대로 진행해서 reindex를 무중단하게 진행하는 방법을 찾아봤다.

 

Alias를 이용하여 reindex하기


기존 index wedul의 매핑구조이다.

PUT wedul 
{
  "mappings": {
    "dynamic": false,
    "properties": {
      "name": {
        "type": "text"
      }
    }
  }
}

해당 인덱스의 데이터는 현재 다음과 같이 들어있는 것을 볼 수 있다. 여기서 age는 매핑이 안되어있어서 검색에 잡을 수 없기에 이를 reindex를 통해 매핑 정보를 업데이트해주자.

wedul 인덱스에 들어있는 데이터(왼), age로 검색이 안됨 (우)

그럼 reindex를 위해 새로운 인덱스 wedul_v1을 만들어보자.

reindex를 진행할 새로운 index, wedul_v1

그리고 wedul_v1으로 reindex를 실행해준다. 이때 주의사항이 있는데 document양이 10만 이상이 넘어가게 되면 작업이 오래걸리기에 kibana에서 504 gateway timeout이 발생하고 작업이 중단된다. 그래서 해당 작업을 비동기로 실행시키는 옵션인 wait_for_completion=false를 함께 설정해주고 진행해야한다.

POST _reindex?wait_for_completion=false
{
  "source": {
    "index": "wedul"
  },
  "dest": {
    "index": "wedul_v1"
  }
}

그럼 위에 이미지처럼 task 프로세스 번호가 나오고 이 프로세스에 시작시간 상태 취소 가능여부 등등을 GET _task 명령어를 통해 볼 수 있다. 여기서 프로세스가 종료되면 reindex가 다 된것이다.

그 다음 wedul_v1에 wedul이라는 alias를 지정해줘야한다. 

POST _aliases
{
  "actions": [
    {
      "add": {
        "index": "wedul_v1",
        "alias": "wedul"
      }
    }
  ]
}

alias를 지정하기 전에 기존 인덱스 wedul을 지워줘야한다. DELETE wedul 명령어를 날려서 기존 인덱스를 지우고 위의 alias 명령어를 실행시킨다. 

그럼 정상적으로 alias를 통해 무중단 reindex를 실행되었다. 정상적으로 실행 되었는지 age에 대한 query를 날려보자.

ㅋㅋ 정상적으로 실행되었다.

앞으로 이런 방식으로 진행해야겠다.

 

출처 : https://discuss.elastic.co/t/reindex-big-index/83047

 

Reindex big index

I would like to reindex a very big index. When I run reindex API with elasticsearchjs client I will receive the requestTimeout error, or Gateway timeout error. It's ok because the reindex process is still running in Elastic server. However, what I want to

discuss.elastic.co

https://www.elastic.co/kr/blog/changing-mapping-with-zero-downtime

댓글()

Elasticsearch nori 형태소 분석기에서 readingform filter를 이용해서 한자 내용을 한글로 변환하기

Elasticsearch filter에서 한자로 검색했을 때 일치하는 한글 결과로 tokenizing하게 해주는 filter가 있다. 해당 filter는 nori-readingform이다. 적용 방법은 기존에 synonmys나 speech필터 적용과 동일하다.

 

인덱스 생성


위에서 부터 사용했던 인덱스에 nori_readingform 필터를 추가해서 생성만 해주면 된다.

PUT wedul_anaylyzer
{
  "settings": {
    "index" : {
      "analysis" : {
        "tokenizer": {
          "nori_user_dict": {
            "type": "nori_tokenizer",
            "decompound_mode": "none",
            "user_dictionary": "dic/nori_userdict_ko.txt"
          }
        },
        "analyzer" : {
          "custom_analyze" : {
            "type": "custom",
            "tokenizer" : "nori_user_dict",
            "filter": [
              "my_posfilter",
              "nori_readingform"
            ]
          }
        },
        "filter": {
          "my_posfilter": {
            "type": "nori_part_of_speech",
            "stoptags": [
              "NP", "UNKNOWN"
            ]
          }
        }
      }
    }
  }
}

이렇게 만든 인덱스를 이용해서 한자를 이용해서 한글 내용을 뽑아내보자

결과


행복이라는 한자를 입력하여 검색해보자. 필터가 정상적으로 적용된다면 행복이라는 내용을 가진 결과가 나올것이다

GET wedul_analyzer/_analyze
{
"analyzer": "custom_analyze",
"text": "幸福 사랑"
}

결과는 정상적으로 행복 그리고 사랑이라는 단어로 추출되었다. nori를 공부하면서 좋은 기본 필터 많은걸 알게 되서 좋다.

댓글()

Elasticsearch 특정 형태소 종류를 제외하여 검색하는 필터 nori_part_of_speech 적용

Elasticsearch를 사용하여 analyze를 사용하다가 조사, 형용사 등등을 제외하고 형태소 토크나이즈가 되어야 했다. 그래서 정식 문서를 찾아보더니 nori_part_of_speech라는 필터가 있었다.

우선 저번 시간에 만들었던 wedul_analyzer 인덱스를 이용해서 토크나이즈를 해보자.

{
  "tokens": [
    {
      "token": "바보",
      "start_offset": 0,
      "end_offset": 2,
      "type": "word",
      "position": 0
    },
    {
      "token": "위들",
      "start_offset": 3,
      "end_offset": 5,
      "type": "word",
      "position": 1
    },
    {
      "token": "이",
      "start_offset": 5,
      "end_offset": 6,
      "type": "word",
      "position": 2
    },
    {
      "token": "집에",
      "start_offset": 7,
      "end_offset": 9,
      "type": "word",
      "position": 3
    },
    {
      "token": "서",
      "start_offset": 9,
      "end_offset": 10,
      "type": "word",
      "position": 4
    },
    {
      "token": "나",
      "start_offset": 11,
      "end_offset": 12,
      "type": "word",
      "position": 5
    },
    {
      "token": "왔다",
      "start_offset": 12,
      "end_offset": 14,
      "type": "word",
      "position": 6
    }
  ]
}

여기서 '나'와 '왔다'를 없애고 토크나이즈 결과가 나왔으면 좋겠다.

그럼 '나'와 '왔다'의 형태소가 어떻게 되는지 우선 알아보자. analyzer api에 explain: true 옵션을 부여하면 해당 토크나이즈에 분리된 형태소들의 정보가 나온다.

GET _analyze
{
  "analyzer": "nori",
  "explain": true, 
  "text": "바보 위들이 집에서 나왔다"
}

'나'와 '왔다'는 NP와 UNKNOWN이다.  이 두개를 nori_part_of_speech필터를 이용해서 제거해보자.

 {
          "token": "나",
          "start_offset": 11,
          "end_offset": 12,
          "type": "word",
          "position": 6,
          "bytes": "[eb 82 98]",
          "leftPOS": "NP(Pronoun)",
          "morphemes": null,
          "posType": "MORPHEME",
          "positionLength": 1,
          "reading": null,
          "rightPOS": "NP(Pronoun)",
          "termFrequency": 1
        },
        {
          "token": "왔다",
          "start_offset": 12,
          "end_offset": 14,
          "type": "word",
          "position": 7,
          "bytes": "[ec 99 94 eb 8b a4]",
          "leftPOS": "UNKNOWN(Unknown)",
          "morphemes": null,
          "posType": "MORPHEME",
          "positionLength": 1,
          "reading": null,
          "rightPOS": "UNKNOWN(Unknown)",
          "termFrequency": 1
        }

custom analyzer를 만들면서 nori_part_of_speech 필터를 추가해주면된다. 이 필터에서 stoptags 배열에 제거하고 싶은 형태소 요형을 추가하면 해당 형태소를 제거한 결과만 출력된다.

PUT wedul_anaylyzer
{
  "settings": {
    "index" : {
      "analysis" : {
        "tokenizer": {
          "nori_user_dict": {
            "type": "nori_tokenizer",
            "decompound_mode": "none",
            "user_dictionary": "dic/nori_userdict_ko.txt"
          }
        },
        "analyzer" : {
          "custom_analyze" : {
            "type": "custom",
            "tokenizer" : "nori_user_dict",
            "filter": [
              "my_posfilter"
            ]
          }
        },
        "filter": {
          "my_posfilter": {
            "type": "nori_part_of_speech",
            "stoptags": [
              "NP", "UNKNOWN"
            ]
          }
        }
      }
    }
  }
}

이렇게 만든 analyze를 이용해서 다시한번 확인해보자. 

아래 결과 처럼 '나'와 '왔다' 두개의 형태소가 사라진 것을 확인할 수 있다.

{
  "tokens": [
    {
      "token": "바보",
      "start_offset": 0,
      "end_offset": 2,
      "type": "word",
      "position": 0
    },
    {
      "token": "위들",
      "start_offset": 3,
      "end_offset": 5,
      "type": "word",
      "position": 1
    },
    {
      "token": "이",
      "start_offset": 5,
      "end_offset": 6,
      "type": "word",
      "position": 2
    },
    {
      "token": "집에",
      "start_offset": 7,
      "end_offset": 9,
      "type": "word",
      "position": 3
    },
    {
      "token": "서",
      "start_offset": 9,
      "end_offset": 10,
      "type": "word",
      "position": 4
    }
  ]
}

 

기본적으로 stoptags를 적용하지 않으면 10몇가지의 형태소 종류가 기본으로 배제된다.

NP, VPC등 형태소들에 대한 용어는 하단 사이트에 잘 정리되어 있다.

https://coding-start.tistory.com/167
http://kkma.snu.ac.kr/documents/?doc=postag

 

꼬꼬마, 한글 형태소 분석기 (Kind Korean Morpheme Analyzer, KKMA)

꼬꼬마 한국어 형태소 분석기 한글 형태소 품사 (Part Of Speech, POS) 태그표 한글 형태소의 품사를 '체언, 용언, 관형사, 부사, 감탄사, 조사, 어미, 접사, 어근, 부호, 한글 이외'와 같이 나누고 각 세부 품사를 구분한다. 대분류 세종 품사 태그 심광섭 품사 태그 KKMA 단일 태그 V 1.0 태그 설명 Class 설명 묶음1 묶음2 태그 설명 확률태그 저장사전 체언 NNG 일반 명사 NN 명사 N NN NNG 보통 명사 NNA no

kkma.snu.ac.kr

 

출처
https://www.elastic.co/guide/en/elasticsearch/plugins/6.4/analysis-nori-speech.html

댓글()
  1. Favicon of https://bodol-engineer.tistory.com BlogIcon 보현94 2020.04.07 14:53 신고 댓글주소  수정/삭제  댓글쓰기

    안녕하세요, Elasticsearch로 개발을 하고 있는 개발자입니다.
    Nori 관련해서 궁금해서 여쭤보려고 합니다.
    현재 제가 user_dict 안에 아래와 같이 넣고 인덱싱 작업을 진행하였습니다.
    - 칼맞은삼겹살, 칼, 맞은, 삼겹살

    decompound_mode는 mixed로 하였으며 복합명사로 만들었습니다. 또한 token 결과는 위의 네개로 분리되어 나오는걸 확인하였습니다.

    다만 5백만개의 데이터에 대해 search API를 사용하여 '칼맞은삼겹살' 검색 시 Exact매칭과 전방매칭을
    제외한 결과는 나오지 않고 있습니다. 제가 원하는 결과는 위 Exact매칭과 전방매칭이 노출 된 이후에 token에 '칼', '삼겹살'이 포함되어 있으므로 예를 들어 '칼먹은삼겹살', '칼삼겹살' 이러한 것도 표출이 되어야 한다 생각하는데 이 부분을 표출하기 위해서는 어떻게 처리해야 될 지 궁금해서 답글 남깁니다.

Elasticsearch에서 Dictionary 변경 시 analyzer와 인덱싱된 Document 갱신 방법

Elasticsearch에서 Dictionary를 사용하여 analyzer를 만들고 그를 사용해서 index에 Document를 인덱싱할 수 있다. 근데 Dictionary가 변경되면 analyzer를 변경하고 indexing된 document를 갱신하려면 어떻게 해야하는지 정리해보자.

Background 지식


Analyzer는 character filter, tokenizer, token filter 순서대로 적용한다. 기본적으로 anaylyzer는 indexing time과 search time에 적용된다. index time 분석 대상은 source data(원본 데이터)이고 search time 분석 대상은 query string이다. 그러므로 사전을 변경하는 것은 indexing, serching 두개 모두 영항을 준다.

사전 업데이트 방법


엘라스틱서치에서 analyzer는 index가 close/open될 때 사전을 읽는다. 그리고 일반적으로 로딩된 이후로는 다시 사전을 읽어 들이지 않는다. 그러므로 수정된 사전을 업데이트 하기 위해서는 dictionary file을 가지고 있는 node를 재시작하거나 index를 _close, _open해야한다.

예를들어 위메프라고 형태소를 나눴을 때, 위메프라는 명사를 알지 못해 다음과 같이 쪼개진다.

GET nori_sample/_analyze
{
"analyzer": "my_analyzer",
"text" : "위메프"
}

anaylze api 결과

그럼 명사라는걸 알려주기 위해서 사전에 위메프를 추가해보자.

그리고 다시 검색을 해보자.
하지만 결과는 처음과 같다. 위에 말한 것 처럼 반영해주기 위해서는 node를 재시작하거나 index를 닫았다가 열어야한다.

다시 검색한 검색결과는 똑같다.

그럼 index를 _close했다가 _open해보자.

POST nori_sample/_close
POST nori_sample/_open


그리고 결과를 다시 확인하면 잘 구분된걸 확인할 수 있다.

정상적으로 변경된 Dictionary가 반영된걸 볼 수있다.

하지만 이 방식으로 사전 업데이트는 이미 인덱싱된 document에는 적용되지는 않는다. 왜냐하면 document는 사전이 업데이트 되기전에 analyzer를 사용해서 인뎅싱 되기 때문이다. 그래서 사전이 업데이트 되었다고해서 사전이 적용되어서 검색결과가 변경되어 나오지는 않는다.

그럼 어떻게 변경된 사전정보를 이미 존재하는 indices에 적용할 수 있을까?

엘라스틱서치에서는 인덱스된 document가 업데이트 되었을 때, document는 제거되고 다시 생성된다. 이때 우리가 업데이트한 사전정보를 이용해서 document가 다시 인덱싱된다. 그렇기 때문에 update by query api를 사용하여 인덱스에 모든 정보를 업데이트해야한다.

update by query 사용방법은 다음과 같다.

update by query 사용방법

 https://www.elastic.co/guide/en/elasticsearch/reference/7.0/docs-update-by-query.html

 

 

출처 : https://www.elastic.co/kr/blog/dictionary-update-behavior-for-elasticsearch-cjk-language-analyzers

댓글()
  1. 최중한 2019.09.04 16:53 댓글주소  수정/삭제  댓글쓰기

    안녕하세요 글 잘봤습니다:)
    궁금한 것이 있는데요, 이 작업을 할 때 리소스를 많이 써야 해서 검색 서비스에 영향을 줄 수도 있다고 하는데
    이걸 회피하는 방법은 어떤 것이 있는 지 궁금합니다.

[번역] Elasticsearch 퍼포먼스 튜닝 방법 - ebay

Elasticsearch에 대해 검색하다가 ebay에 퍼포먼스 튜닝방법에 대해 좋은 글이 있어서 간단하게 정리해봤다. 새롭게 알게된 사실이 많아서 좋았다. 

정리 잘된 기술 블로그를 보는것은 책을 읽는거보다 훨씬 유익한 경우가 많은 것 같다.

 

Elasticsearch


엘라스틱 서치는 아파치 루씬을 기반으로한 검색과 분석 엔진으로 데이터를 실시간에 가깝게 보여주고 분석해 준다. 실시간성으로 분석과 검색을 위해서 많이 사용되는 엘라스틱 서치의 퍼포먼스는 무엇보다 중요한데 이를 위한 퍼포먼스 튜닝방법을 정리해보자.

높은 엘라스틱서치의 퍼포먼스를 위해서는 많은 처리량, 낮은 검색 지연시간등이 요구된다.

 

고효율성 Elasticsearch를 위한 솔루션


- 효율 적인 인덱스 디자인
인덱스를 설계하다보면 하나의 인덱스에 모든 데이터를 넣고 쿼리로 찾을것인지 아니면 여러 인덱스로 나눌것인지 고민된다. 어느것이 효율적이냐는 정답은 내가 사용하는 쿼리에 달려있다. 유형별로 사례를 보면서 확인해보자.

1. 쿼리에 filter가 들어가고 그 값이 Enumerable할 때는 인덱스를 나눠서 설계하라
만약 인덱스 내부에 데이터에서 지역별로 나눠서 데이터를 찾아야 한다면 다음과 같이 쿼리를 실행시킬 것이다. 이럴경우 지역별로 인덱스를 구분하여 만들면 더욱 효율적인 퍼포먼스를 기대할 수 있다.

{
    "query": {
        "bool": {
            "must": {
                "match": {
                    "title": "${title}"
                }
            },
            "filter": {
                "term": {
                    "region": "US"
                }
            }
        }
    }
}

 

2. 값이 enumerable하지 않다면 routing key를 사용하라.
만약 수백만개의 주문 데이터가 구매자별로 쌓인다고 가졍하였을때 구매자별로 인덱스를 구분한다면 너무 많은 인덱스를 구분해야한다. filter 사용되는 필드를 routing key로 사용하여 인덱스를 여러 shard로 쪼갤 수 있다. 그래서 이럴 경우 동일한 buyerId를 가지고 있는 데이터를 동일한 shard에 모여서 저장하면 모든 쿼리가 buyerId라는 routing key를 사용하여 한번에 조회되게 하면 퍼포먼스가 향상된다.

3. 기간이 정해져 있는 데이터들의 경우 기간별로 인덱스를 구성하여 사용하라.
로깅, 모니터링을 하는 데이터의 경우 일, 주, 월별로 데이터를 모을 수 있기 때문에 이를 사용하여 날짜별로 데이터를 모으면 더 빠르게 데이터에 접근할 수 있다.

4. mapping을 효율적으로 지정하라
인덱스에서 필드별로 mapping을 지정해줘야 해당 필드에 대한 검색이 가능하다. 기본적으로 데이터가 인덱스에 들어가면 기본적으로 자동 매핑을 해주지만 효율적이지는 않다. 그래서 정상적으로 인덱스에 맞게 효율적으로 인덱스를 설계하고 index생성시 dynamic옵션을 false로 지정하여 자동 매핑을 방지하는게 좋다. 자동매핑에 경우 문자열을 text, keyword모두 지정해주는데 이는 그닥 효율적이지 못한 대표적인 예이다.

5. 사용자 정의 id를 사용할 경우 불균형 샤딩에 대해 조심하라.
엘라스틱 서치는 자동으로 id를 생성해주는데 이를 호ㄹ용하여 적절하게 document들에 대하여 샤딩을 수행한다. 만약 사용자 지정 id를 사용하거나 별도의 routing key를 충분히 랜덤하지 않게 부여한다면 다른 shard들에 비해 특정 shard가 데이터가 많이 적재되는 문제를 초래할 수 있다. 이럴 경우 일기/쓰기가 굉장히 느려지게 되기 때문에 조심해야한다.

6. 여러 노드에 고르게 shard를 만들어라.
엘라스틱서치에는 coordinator node와 여러 마스터 노드들로 구성되어 있는데 coordinator 노드는 분배 역할을 주로 담당하고 master node들이 데이터 적재를 담당하는데 각 특정노드에 shard가 몰려있을 경우 전체 시스템의 bottle neck 현상이 발생할 수 있다.


7. bulk request를 높이고 refresh 기간을 올려라.
이벤트가 발생할 때마다 엘라스틱서치는 새로운 lucene segment를 만들고 그것을 추후에 합치는 작업을 진행한다. 이런 작업이 발생하는 주기가 refresh가 발생할 때 진행되는데 이런 문제를 줄이기 위해서는 refresh interval을 길게 잡아 놓으면 좋다. 하지만 refresh interval이 길면 변경된 데이터가 검색이 되지 않는다. 왜냐면 index가 refresh가 종료되어야 변경되거나 추가된 document가 검색에 잡히기 때문이다.


8. replica 수를 줄여라.
엘라스틱서치 노드는 주요 노드에 기록되고 모든 replica shard들에 인덱싱 요청이 발생된다. 그렇기 때문에 replica shard수가 많을수록 document의 인덱싱 속도가 현저하게 느려진다.


9. 가능하면 자동으로 생성되는 ID를 사용하라.
엘라스틱서치에서 만들어주는 ID는 고유값을 보증한다. 만약 사용자 고유 ID를 진행하고 싶은 경우 루신에 친화적인 zero-paddeded sequaltial 아이디인 UUID-1 또는 Nono time을 사용해야 한다. 이 아이디들은 일관되고 연속적인 패턴이 잘 압축되어 있는 형식들이다. 대조적으로 UUID-4는 완전하게 랜덤한값을 만들어내기 때문에 추천하지 않는다.


10. 가능하면 query context에 filter를 사용해라.
쿼리에서 찾고자 하는 값과 일치하는 걸 찾을 때 대부분인 yes or no로 찾을 수 있다. match등을 통해서 데이터를 찾으면 scoring이 들어가기 때문에 더 성능적으로 힘들어진다. 하지만 filter를 사용하면 결과가 캐시가 되는것뿐만 아니라 단순 yes or no이기 때문에 score 계산이 빠져서 더 효율적이다.


11. shard 노드를 효율적으로 관리하라
인덱스에 어느정도에 shard를 설정해야할까가 인덱스 구축시 고민을 많이 하게되는 요소이다. 불행하게도 모든 시나리오에 정확한 정답은 없다. 모든것은 어떻게 설계하냐에 따라 달려있다. 만약 너무 작은 수의 shard를 설정하게 되면 scale out이 되지 않는다. 예를들어 오직 하나의 shard만 있는 경우 모든 도큐먼트는 한곳에 저장되서 문제가 발생한다. 반대로 너무 많은 shard를 설정하게 되면 퍼포먼스에 문제가 발생한다. 왜냐하면 엘라스틱서치는 모든 shard에 쿼리를 실행시키기 때문에 request안에 routing key가 있다고 하더라도 fetch 그리고 merge가 모든 shard에서 나온 결과에서 발생하기 때문이다. 여러 관례로 봤을 때, 만약 index가 1G가보다 작으면 shard는 1개가 적당하다. 대부분에 경험상 shard를 5섯개를 기본으로 놓고 운영하는게 적당했다. 하지만 shard사이즈가 30GB가 넘으면 더 쪼개서 사용해야한다. 한번 생성된 index에 shard수를 조정하려면 reindex를 해야하므로 신중하길 바란다.


12. stop word를 사용한 검색을 자제하라
stop word는 a, the와 같이 검색 결과를 증가시키는 단어들이다. 만약 fox라고 검색하면 10건나올 데이터가 the fox라고 하면 the와 가까운 데이터가 출력되면서 거의 대부분의 document가 출력될 것이다. 엘라스틱서치는 모든결과를 score별로 정렬하기 때문에 the fox와 같이 stop word로 검색하면 전체 시스템을 느리게 만든다. 미리 token filter를 만들어서 stop word를 제거하고 검색되게 만드는게 효율적이다. 만약 당장 어렵다면 the ADN fox라고 검색해서 중요한 결과만을 뽑아낼 수 있다.

 

오랜만에 좋은글 잘 읽어서 기분이 좋다. 

출처 : https://www.ebayinc.com/stories/blogs/tech/elasticsearch-performance-tuning-practice-at-ebay/

댓글()
  1. wedul 2019.08.13 18:11 댓글주소  수정/삭제  댓글쓰기

    shard는 데이터 자체를 의미하고 replica shard는 백업을 위한 shard가 아니라 failover를 위한 shard이기 때문에 replica shard를 많이 설정하는건 안좋다.

Elasticsearch template를 일별 index 구성하기

Elasticsearch를 이용해서 로그기록을 많이한다. 일별 로그성 인덱스를 자동으로 만들기 위해 template를 사용해서 구성하는 법을 정리해보자.

우선 매일 새롭게 생성될 index에 대한 template를 생성해보자.

template 생성


PUT _template/wedul_log_*
{
  "index_patterns": [
    "wedul_log_*"
  ],
  "mappings": {
    "_doc": {
      "dynamic": false,
      "properties": {
        "id": {
          "type": "integer"
        }
      }
    }
  }
}

생성한 템플릿이 잘 만들어졌는지 확인해보자.

확인
GET template/wedul_log*

그럼 이제 wedul_log라는 대표 인덱스를 생성해주고 template에 맞는 일자별 인덱스를 만들어보자.

PUT wedul_log
{
  "mappings": {
    "_doc": {
      "dynamic": false,
      "properties": {
        "id": {
          "type": "integer"
        }
      }
    }
  }
}

인덱스 생성이 완료되면 template에 맞게 데이터를 삽입해보자.

일별 데이터 추가

POST wedul_log_2019.06.12/_doc
{
	"id": 1
}

POST wedul_log_2019.06.13/_doc
{
	"id": 2
}

6월 12, 13일에 대한 로그 인덱스가 생겼고 document가 생성되었다.

이런식으로 일자별 인덱스를 자동으로 생성하게 할 수 있다.

여러 로그성 인덱스를 한번에 조회를 다음 명령어를 통해 할 수 있다.

GET wedul_log_2019.06.12,wedul_log_2019.06.13/_search

댓글()

Elasticsearch에서 reindex를 이용해서 매핑정보 변경하기

Elasticsearch에서 index를 구성하다보면 매핑정보를 추가하거나 수정하고 싶을때가 있다.  내가 아는 내에서는 한번 생성된 index의 매핑정보를 변경하는건 어렵다. 그래서 reindex를 통해 index의 매핑정보를 변경해줘야한다.

우선 wedul_mapping이라는 인덱스가 있다고 해보자.
매핑 정보는 다음과 같다.

PUT wedul_mapping
{
  "mappings": {
    "_doc": {
      "dynamic": "false",
      "properties": {
        "id": {
          "type": "integer"
        },
        "name": {
          "type": "text",
          "fields": {
            "keyword": {
              "type": "keyword"
            }
          }
        }
      }
    }
  }
}

이때 name에서 keyword필드를 제거하고 age라는 새로운 Integer타입의 필드를 매핑하고 싶은 경우에 wedul_mapping_dump라는 새로운 임시 인덱스를 생성한다.

PUT wedul_mapping_dump
{
  "mappings": {
    "_doc": {
      "dynamic": "false",
      "properties": {
        "id": {
          "type": "integer"
        },
        "name": {
          "type": "text"
        },
        "age": {
          "type": "integer"
        }
      }
    }
  }
}

그리고 기존 wedul_mapping인데스에서 wedul_mapping_dump 인덱스로 reindex를 실행한다.

POST _reindex
{
  "source": {
    "index": "wedul_mapping"
  },
  "dest": {
    "index": "wedul_mapping_dump"
  }
}

 

그럼 데이터가 모두 변경된 인덱스 wedul_mapping_dump로 복사되면서 매핑정보가 변경된것을 알 수 있다. 그리고 이름이 같은 wedul_mapping인덱스에 다시 옮기려면 wedul_mapping인덱스를 제거하고 변경된 매핑정보로 새로 생성한뒤 다시한번 reindex를 해주면된다. 데이터가 많은 실 환경에서는 reindex 작업의 비용이 크기 때문에 한번 매핑정보를 설정할 때 잘해주는것이 좋을 것 같다.

댓글()