'데이터베이스/Elasticsearch'에 해당되는 글 19건

데이터베이스/Elasticsearch

Elasticsearch에서 synonyms.txt로 동의어 필터 만들어서 사용하기

elasticsearch에서 검색기능을 넣다가 2080이라는 키워드를 검색 했을때와 이공팔공이라는 검색어를 입력했을 때 두개 모두 동일한 데이터를 출력하도록 지정하고 싶었다.

그래서 synonyms 필터를 만들기로 했다.

우선 synonyms 필터를 만들어서 사용하기 위해서는 동의어에 대한 정리가 되어있는 사전을 만들어야 한다.

사전 생성 방법은 다음과 같고 아래 링크를 참조해서 간단하게 사전을 만들었다.
https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-synonym-tokenfilter.html

파일명은 synonyms.txt이고 내용은 다음과 같다.

synonyms.txt

노레바,noreva,노래바
airpods,에어팟,airpod,airpot
2080,이공팔공

 

동의어 정리 사전을 elasticsearch에 넣어줘야하고 경로는 다음과 같다.

/usr/share/elasticsearch/config/analysis



나는 docker를 사용하기 때문에 생성한 파일을 다음 명령어를 통해 위치시켰다.

docker cp synonyms.txt elastic6.4:/usr/share/elasticsearch/config/analysis



그럼 이 필터를 사용하기 위해서 인덱스를 만들 때 설정을 달아서 추가해주자.
간단하게 keyword 하나만 존재하는 인덱스를 만들고 synonyms filter를 사용하게 해보자

여기서 사용하 tokenizer는 nori_tokenizer를 사용했다.
간단한 예제니 보면 바로 이해할 수 있다.

PUT synonyms_test
{
  "settings": {
      "index" : {
          "analysis" : {
              "analyzer" : {
                  "synonym" : {
                      "tokenizer" : "nori_tokenizer",
                      "filter" : ["synonym"]
                  }
              },
              "filter" : {
                  "synonym" : {
                      "type" : "synonym",
                      "synonyms_path" : "analysis/synonyms.txt"
                  }
              }
          }
      }
  },
  "mappings": {
    "_doc": {
      "dynamic": "false",
      "properties": {
        "keyword": {
          "type": "text",
          "analyzer": "synonym",
          "search_analyzer": "synonym",
          "fields": {
            "keyword": {
              "type": "keyword"
            }
          }
        }
      }
    }
  }
}

데이터를 2080과 이공팔공 두개를 삽입했다.

POST synonyms_test/_doc
{
  "keyword":"2080"
}

POST synonyms_test/_doc
{
  "keyword":"이공팔공"
}

그럼 이 두개의 키워드가 동일하게 하나의 명령어로 검색이 되는지 확인해보자.

GET synonyms_test/_search
{
  "query": {
    "match": {
      "keyword": "이공팔공"
    }
  }
}

2080과 이공팔공으로 검색한 결과

데이터베이스/Elasticsearch

elasticsearch 7.0 docker 설치 후 변경사항 확인

엘라스틱서치 7.0이 출시했다.

엘라스틱서치 7.0에는 kibana UI변경과 multi mapping type 제거 등의 이슈가 있다.

우선 달라진점을 확인하기 위해 docker에 설치해보자.

설치

elasticsearch

docker run --name elastic7.0 -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" docker.elastic.co/elasticsearch/elasticsearch:7.0.0

 

kibana

docker run -d --rm --link elastic7.0:elastic-url -e "ELASTICSEARCH_HOSTS=http://elastic-url:9200" -p 5601:5601 --name kibana7.0 docker.elastic.co/kibana/kibana:7.0.0

설치가 끝나고 프로세스를 확인해보면 elastic과 kibana가 올라가 있는 것을 확인할 수있다.

 

kibana UI 확인

키바나에 접속해 보니 엄청 깨끗하다. 몬가 되게 플랫해진 요새 디자인을 하고 있다. 5부터 6까지 변할때도 몬가 키바나가 멋있어졌다고 느꼈는데 이번에 7버전은 더 멋진 것 같다.

그리고 더 대단한 건 요새 대세인 다크모드가 지원된다.

다크모드는 setting > kibana > advanced settings > dark mode 위치에 가면 다음과 같은 설정 하는 부분이 있다.

설정 후 새로고침을 하면 적용되는데 화면이 멋있다.

 

그리고 멀티 맵핑 타입이 제거되고 등등 다른 변경사항이 있는데 아래 링크를 확인하면 된다.

근데 릴리즈하면서 마지막 글에 보면 기존에 elastic을 6버전 이상 사용하고 있으면 굳이 업데이트 할 필요는 없다고 한다 ㅋㅋ

 

변경사항

https://www.elastic.co/blog/elastic-stack-7-0-0-released

데이터베이스/Elasticsearch

docker logstash 설치 및 log 파일 elasticsearch에 기록

ELK에서 logstash를 제외하고는 모두 경험해봤다.

이제 logstash를 사용해서 log파일을 elasticsearch에 기록해보자.


설치

elasticseach도 kibana도 pc에 직접 설치하고 싶지 않아서 docker에 설치해서 사용했다. logstash도 docker에 설치해서 사용해보자.


물론 logstash를 사용하기전에 elasticseach와 kibana가 설치되어 있어야한다. 설치법은 저번 게시물에 올려놨다.


logstash를 이름을 지정해서 background에서 동작하도록 실행시킨다.

1
docker run --name logstash -d docker.elastic.co/logstash/logstash:6.4.0
cs


설정파일

logstash를 설치하면 내부에 다음과 같은 설정파일이 존재한다.

 이름

 설명

logstash.yml 

 logstash 구성 플래그가 들어있다. 이곳에 설정하면 command에 직접 설정할 필요가 없다. 그래도 command명령이 logstash.yml 파일의 우선순위보다 높다.

 pipelines.yml

 single logstash 인스턴스에서 파이프라인을 실행시키기 위한 설정내용을 담고 있다. input, filter, output등을 설정한다,

 jvm.options

 jvm 설정을 포함하고 있어서 힙사이즈 조절이 가능하다.

 log4j2.properties

log4j 설정을 할 수있다. 


설치가 완료된 후 logstash를 기존에 설치된 elasticsearch에 연동해서 상태를 보려고 할때 오류가 발생한다. config/logstash.yml에서 호스트 네임을 설정해주면 정상적으로 연결이 되고 monitoring까지 할 수 있다.



파이프라인

logstash에서 파이프라인은 input, output, filter가 존재한다. input과 output은 데이터 입출력을 위해 필수값이고 filter는 정재하기 위한 설정으로 선택값이다.

파이프라인 설정은 /pipieline 내부에 conf파일을 생성하여 설정할 수있다.


로그기록

그럼 설치된 logstash에서 특정 로그파일을 읽어서 elasticsearch에 기록해보자. pipeline.yml 파일을 수정해서 다음과 같이 기록한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
input {
  file {
    path => "/wedul.log"
    start_position => "beginning"
    sincedb_path => "/dev/null"
  }
}
 
output {
  elasticsearch {
    hosts => "http://we.local:9200"
    index=>"wedulpos"
  }
}
cs


위 내용은 파일을 읽고 elasticsearch wedulpos인덱스에 저장하라는 내용이다. start_position이 beginning이면 처음부터 읽으라는 것이고 sincedb는 내부적으로 어디까지 읽었는지 확인하기위해 저장하는 db이다. 현재 테스트할때 sincedb 설정이 제대로 되지 않아 No sincedb_path set, generating one based on the "path" setting 오류가 발생하여 우선적으로 /dev/null로 진행했다.

설정이 끝나고 logstash를 실행시키면 다음과 같이 저장되는걸 알 수있다.


모니터링에서도 해당 로그 기록 상황을 볼 수있다.


이 밖에도 input, output을 jdbc, cloudwatch등으로 설정해서 진행할수도 있다.




데이터베이스/Elasticsearch

Elasticsearch에서 refresh 정리

Elasticsearch에서 document를 업데이트하고 바로 해당 정보를 조회하려고 했다.

하지만 조회가 되지 않았다. 분명이 업데이트가 종료된 것을 확인 했는데 왜 그런지 의문이 들었다.


그래서 찾아봤는데 document가 업데이트가 되고나서 인덱스에서 실제로 조회가 될 수있는 상태가 되기위해서는 일정시간이 필요한 것 같다.

자세히는 모르지만 다시 인덱싱을 걸기 때문에 그러는건 아닌가 생각된다.


그래서 이런경우에 업데이트가 종료 되었다고 알리는 시간을 검색이 가능하게 변경된 시간까지 포함해서 알려주도록 하는 옵션이 존재한다.


그렇게 되면 업데이트가 되고 검색이 가능한줄 알고 프로그램을 작성하다가 버그가 발생하는 비율을 줄일 수 있다.


일반적인 bulkInsert나 update, create같은 명령에는 refresh: wait_for를 사용하고 update by query 등에 명령어에서는 waitForCompletion=true 옵션을 부여하여 검색을 사용한다.


예시

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
client.bulk({
refresh: wait_for,
  body: [
    // action description
    { index:  { _index: 'myindex', _type: 'mytype', _id: 1 } },
     // the document to index
    { title: 'foo' },
    // action description
    { update: { _index: 'myindex', _type: 'mytype', _id: 2 } },
    // the document to update
    { doc: { title: 'foo' } },
    // action description
    { delete: { _index: 'myindex', _type: 'mytype', _id: 3 } },
    // no document needed for this delete
  ]
}, function (err, resp) {
  // ...
});
cs

api 문서를 보면 조금 더 정확한 내용을 확인할 수 있다.


참고

https://stackoverflow.com/questions/40676324/elasticsearch-updates-are-not-immediate-how-do-you-wait-for-elasticsearch-to-fi

https://www.elastic.co/guide/en/elasticsearch/client/javascript-api/current/api-reference.html



주의

하지만 이렇게 프로그램을 작성할 경우에는 예상하는 바와 같이 엄청난 시간이 소요된다.

실제로 이번에 프로젝트를 진행할 때 해당 옵션없이 업데이트를 할 때 5분걸렸던 양이 1시간이 되어도 되지 않았다. 이럴 경우에는 하나하나 다 update 명령을 사용하지말고 bulk로 작업하는 것을 추천한다. 훨씬빠르다.



데이터베이스/Elasticsearch

Elasticsearch에서 search_after 기능 사용하여 조회하기

elasticsearch에서 search_after를 이용하여 데이터를 조회하는 방법을 정리해보자.

우선 사용할 인덱스를 생성하자.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
PUT wedul
{
  "mappings": {
    "cjung": {
      "properties": {
        "id": {
          "type": "keyword"
        },
        "name": {
          "type": "text",
          "analyzer": "nori",
          "fields": {
            "keyword": {
              "type": "keyword",
              "ignore_above": 256
            }
          }
        }
      }
    }
  }
}
cs


생성된 인덱스에 데이터 몇개만 삽입하여보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
POST wedul/cjung?pretty { "id": "wemakeprice", "name": "원더쇼핑" } POST wedul/cjung { "id": "dauns", "name": "다운" }
cs


그리고 일반적으로 사용하는 방식으로 데이터를 조회해보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
GET wedul/cjung/_search
{
  "from": 0, 
  "size": 2, 
  "query": {
    "match_all": {}
  }
}
 
 
{
  "took" : 8,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 3,
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "wedul",
        "_type" : "cjung",
        "_id" : "_update",
        "_score" : 1.0,
        "_source" : {
          "doc" : {
            "id" : "wedul",
            "name" : "정철"
          }
        }
      },
      {
        "_index" : "wedul",
        "_type" : "cjung",
        "_id" : "tSNYH2cBvWxWFgHQJ6J4",
        "_score" : 1.0,
        "_source" : {
          "doc" : {
            "id" : "dauns",
            "name" : "다운"
          }
        }
      }
    ]
  }
}
 
cs

정상적으로 조회가 된다. 하지만 여기서 만약 size가 10000이 넘은 곳을 검색하고 싶다면 어떻게 될까? 저번에 공부해서 정리한 글 처럼 10000개 이상에 데이터에 접근하려고 하면 오류가 발생한다.

참고 : https://wedul.tistory.com/518?category=680504


그럼 어떻게 조회해야 할까? 그래서 제공되는 방법이 search_after를 이용하여 검색하는 방법이다.

search_after는 라이브 커서를 제공하여 다음 페이지를 계속 조회하는 방식으로 검색기능을 제공한다. 


기본적인 검색 방법은 다음과 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
GET wedul/cjung/_search
{
  "sort": [
    {
      "id": {
        "order": "asc"
      },"name.keyword": {
        "order": "desc"
      }
    }
  ], 
  "size": 1, 
  "query": {
    "match_all": {}
  }
}
cs


이렇게 검색을하게 되면 다음과 같이 결과가 나오는데 여기서 나온 sort  필드를 이용하여 다음 필드를 조회해 나가는 것이 search_after 기능이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
{
  "took" : 9,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 1,
    "max_score" : null,
    "hits" : [
      {
        "_index" : "wedul",
        "_type" : "cjung",
        "_id" : "uyNoH2cBvWxWFgHQ86L9",
        "_score" : null,
        "_source" : {
          "id" : "wemakeprice",
          "name" : "원더쇼핑"
        },
        "sort" : [
          "wemakeprice",
          "원더쇼핑"
        ]
      }
    ]
  }
}
 
cs


위에 나온 검색결과 sort에 출력된 wemakeprice와 원더쇼핑을 사용하여 다음 데이터를 조회한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
GET wedul/cjung/_search
{
  "search_after": ["wemakeprice",
          "원더쇼핑"],
  "sort": [
    {
      "id": {
        "order": "asc"
      },"name.keyword": {
        "order": "desc"
      }
    }
  ], 
  "query": {
    "match_all": {}
  }
}
cs


그렇다면 저 위에 sort 필드는 과연 무엇인가 하고 생각이 들 수 있다. sort 필드는 바로 검색 dsl에서 사용했던 sort필드의 값 들이다. 이 값 다음에 나오는 데이터를 조회 하라는 뜻이다. 그렇기 때문에 무조건 search_after를 사용하기 위해서는 데이터를 정렬하는것이 필수이다. 그리고 정말 중요한 것은 그 sort필드에 들어가는 데이터중 하나는 무조건 unique한 값 이어야 한다는 것이다. 그렇지 않으면 어디서 부터 검색을 시작해야할지 알지 못하기 때문이다. 


참고

https://www.elastic.co/guide/en/elasticsearch/reference/master/search-request-search-after.html

 [ 1 ]  [ 2 ]  [ 3 ]  [ 4 ] 

푸터바

알림

이 블로그는 구글에서 제공한 크롬에 최적화 되어있고, 네이버에서 제공한 나눔글꼴이 적용되어 있습니다.

카운터

  • Today : 13
  • Yesterday : 460
  • Total : 82,704