엘라스틱 서치를 들어가기 전에 일단, 색인과 역색인에 대한 개념을 잡고 가야한다.
https://develop-park.tistory.com/42
루씬이 먼가요?
루씬은 자바로 개발된 고성능 정보검색 오픈소스 라이브러리 입니다. 1999년 하둡개발자로 잘 알려진 더그 커팅이 개발했고 2005년 아파치 톱 레벨 프로젝트로 승격됐습니다. 가장 유명한 무료 자
develop-park.tistory.com
색인이란, 문서를 기준으로 텀을 나열한 것.
역색인이란, 텀을 기준으로 문서를 나열한 것이다.
따라서 어떠한 키워드를 검색했을 때
색인의 구조에선 모든 문서를 다 일일이 살펴보며 그 키워드가 있는지 확인해야하지만,
역색인의 구조에선 그 키워드에 나열된 문서들만 살펴보면 되기 때문에 검색 속도가 엄청 빠르다.
이렇게 데이터를 저장하고 검색하는 엔진으로 대표적인 것이 ‘루씬’이다.
루씬은 색인과 역색인의 방법으로 데이터를 저장/검색하는 기능을 제공한다.
그리고 이러한 루씬을 코어 엔진으로 하여, 데이터의 검색 등을 효율적으로 돕는 REST API, 샤드 기능 등등을 넣어서 만들어 진 것이
엘라스틱 서치다. 따라서, 실제로 데이터를 저장하는 것은 코어엔진 추가 기능은 엘라스틱 서치가 붙인 기능이다.
- 엘라스틱서치는 아파치 루씬이라는 분산검색 엔진을 기반으로 하여 탄생하였다.
아파치 루씬이란 색인/검색 전용 라이브러리다.
- 엘라스틱서치에 로그스태시 및 키바나를 합쳐 ELK라고 칭한다. 이를 하나로 칭함으로써 JSON 데이터의 수집, 변환, 저장, 색인, 검색 및 시각화까지 하나의 시스템에서 다 구현 가능하다.
- 검색 엔진을 기반으로 하였기 때문에 다른 NoSQL보다 압도적으로 빠른 검색 기능을 지원한다. 그 외 특징으로는 분산처리, HA, Scale-Out, JSON 기반의 REST API, 조인없음, 트랜잭션 없음 이라는 특징이 있다.
- JSON 기반의 REST API란, 클라이언트 입장에서 특별한 설치없이 HTTP 패킷만 만들어서 부르면 색인, 조회, 변경, 검색, 삭제까지 모두 가능하다.
1. 색인 (= 인덱스 넣어 데이터 create)
# 색인 (인덱스 넣어 DATA CREATE)
PUT [인덱스명]/_doc/[_id값]
{
"key" : "value"
}
# 또는 _id 값을 생략할 수도 있음 -> 생략시, 엘라스틱에 생성하여 자동 삽입함
# _id값을 생략하려면 PUT이 아닌 POST로 날려야 한다.
POST [인덱스명]/_doc
{
"key" : "value"
}
# 조회
GET [인덱스명]/_doc/[_id값]
# 변경(UPDATE)
POST [인덱스명]/_update/[_id값]
{
"doc" :
{
"key" : "value"
}
}
# 검색 (GET 대신 POST 사용도 가능)
GET [인덱스명]/_search
{
"query" : {
"match" : {
"key" : "value"
}
}
}
# 삭제
DELETE [인덱스명]/_doc/[_id값]
- 검색을 했을 때, 결과로 나오는 값이 조금 신기.. 독특..
value에 100% match가 아니라 '유사한' 키워드는 다 나옴. 그리고 score라는 결과도 함께 나오는데 얼마나 유사한지를 보여준 것.
예를 들어 match : { "title" : "hello world" } 를 검색하면 "Hello Beyonce" 도 나오고 "Disney World"도 나옴 ㅋㅋ와 웅
-- 엘라스틱 서치의 구조 --

- Document : 하나의 JSON 값
- 인덱스: Document를 모아놓은 단위 (검색의 단위가 되기도 함)
- 타입: 인덱스 내에서 Document를 묶는 논리적 단위이나, 현재 Ver6 부터는 지원되지 않음
- 노드: 엘라스틱서치 프로세스 1개가 노드 하나를 구성함. 하나의 노드 내엔 여러 개의 샤드가 속해있으며 노드는 마스터, 데이터, 조정 등의 역할 중 한가지 이상을 갖는다. 마스터는 클러스터를 관리하고 데이터는 실제 데이터를 R/W하는 노드이며 조정은 클라이언트의 Request를 받아 적절한 요청에 라우팅한다.
- 위 구조를 짧게 설명하면 가로줄은 각 노드다. 노드 4대에 각 샤드들이 들어가있음. 샤드 내엔 여러 개의 Document가 속해있으며 이 Document들은 구분에 따라 특정 인덱스에 속한다. 데이터는 복제 관계를 갖고 있으므로 하나의 노드에 Insert가 발생하면 다른 노드로 복제된다.
- 루씬은 엘라스틱 서치가 사용하는 기본 코어 엔진이다.
- 루씬의 Flush
색인 요청이 들어오면 루씬은 해당 문서를 분석한 후 역색인을 생성하고 임시적으로 메모리 버퍼에 들어가게 된다. 그 후, 주기적으로 디스크로 Flush 된다.
루씬은 검색을 하기 위해선 디스크에서 해당 파일을 열어서 검색을 하는데, Open시점까지 저장이 완료되어 있는 문서에 대해서만 검색이 가능하다.

- 라우팅
문서를 색인/검색 등 할 때, 어떤 샤드를 대상으로 할지 지정해줄 수 있는 기능이다.
예를 들어, 색인할 때 ‘myid’라는 값을 함께 라우팅 조건으로 주면 해당 문자열을 해쉬한 후 나온 샤드에 값을 저장한다.
검색할 때도 마찬가지로 해당 값을 함께 넣어주면, 다른 샤드를 조사하지 않고 하나의 샤드에서만 데이터를 찾아 반환하기 때문에 속도가 빨라진다.
단, 조건을 잘못 주어 다른 샤드가 대상이 되어버리면 실제 그 데이터가 다른 샤드엔 존재하지만 검색 결과에는 안나올 수 있게 된다.
운영 시, 라우팅을 활용한다면 성능향상에 도움이되므로 사용하는 편이 권장되며 색인시 이 값을 강제할 수 있도록 설정할 수도 있다.
- doc_value, datafield
문자열을 저장하는 자료형에는 keyword, text 가 있다.
keyword는 문자열을 그대로 색인한다면, text는 문자를 term 이라는 단위로 쪼개 term을 저장한다.
keyword는 정렬과 집계, 스크립트 작업을 처리할 때 좋고 text는 전문 검색에 적합하다.
keyword는 기본적으로 doc_values 라는 캐시를 사용하고, text는 fielddata라는 캐시를 사용하기 때문이다!!
기본적으로 엘라스틱 서치의 검색은 역색인을 기반으로 한 색인을 이용한다.
텀을 보고 역색인에서 문서를 찾는다. (여기서 term은 hello/world 처럼 쪼개진 한 단어를 뜻하고 문서는 전체, hello world! 를 뜻함)
검색 조건을 텀으로 짜른 다음에 텀에 해당하는 문서를 찾는다는건가?
그러나 집계, 정렬, 스크립트 작업 시엔 접근법이 좀 다르다. 문서를 보고 필드 내의 텀을 찾는다.
doc_value : 디스크를 기반으로 한 자료구조 파일 시스템 캐시.?
fielddata: text 타입은 파일 시스템 기반의 캐시인 doc_values를 사용할 수 없다. fielddata를 사용한 정렬/집계 등의 작업 시에는 역색인 전체를 읽어들여 힙 메모리에 올린다. 이러한 동작방식은 메모리를 순식간에 차지하여 oom 을 일으킬 수 있음. 얘는 기본적으로 비활성화 상태다.
즉, 검색 등을 할 때 두가지의 방식 중 어떤 방식을 사용할지 정할 수가 있는데.
첫번째 방식을 사용하면, 응?
두번응?
잘 모르겠ㄴ는뎅
정렬, 집계, 필드의 접근 등의 방식은 그 필드의 값만 필요하지 그 필드가 가리키고 있는 문서 전체를 볼 필요가 없다.
따라서, 역색인된 데이터 전체가 필요하진 않다.
이와 같은 상황에서 사용할 수 있는 캐싱 방법으로 두가지가 있는데 그게 닥밸류, 필드데이터가 있는 것이다.
필드 데이터는 모든 필드의 값을 한번에 메모리에 올리지만
닥 밸류는 이걸 스토리지에 올리는 것.
그 차이일까?
텍스트 타입과 키워드 타입.
문자열 자료를 담는 필드에는 텍스트 타입 / 키워드 타입 중 하나를 선택해 적용할 수 있다.
텍스트는 애널라이저가 적용된 후, 색인된다.
문자열이 들어오면, 값을 분석하여 여러 토근으로 쪼갠다. 이렇게 쪼개진 토큰으로 역색인을 구성한다.
쪼개진 토큰에 지정한 필터를 적용하는 등의 후처리 작업 후, 최종적으로 역색인에 들어가는 형태를 텀(term)이라고 한다.
반면 키워드는 문자열을 여러 토큰으로 쪼개지 않고 역색인을 구성한다. 애널라이저로 분석하는 대신 노멀라이저를 적용한다.
그 후ㅡ 단일 텀으로 쪼개지 않고 그 문자열 자체로 역색인을 구성한다.
키워드 검색을 할 때도 그냥 쓰는게 아니라 “match” 라는 특별한 쿼리를 추가로 사용해야한다.
GET mapping_test/_search
{
“query”: {
“match”: {
“keywordString”: “the world said hello”
}
}
}
match 쿼리는 검색 대상 필드가 text인 경우에, 검색 질의어도 애널라이저로 분석한다.
애널라이저가 위 텍스트를 the, world, said, hello로 4개의 텀으로 쪼갠다.
그리고 이 4개의 텀을 역색인에서 찾는다.
텍스트와 키워드는 색인 방식에 차이가 있어 텍스트 타입은 주로 전문 검색에 적합하고 키워드는 완전 일치 검색에 적합하다.
정렬/집계/스크립트 작업 대상이 될 필드 검색 :: 키워드 타입
왜냐면, 기본적으로 키워드는 doc_values라는 캐시를 사용하고
text 타입은 fielddata라는 캐시를 사용하기 때문이다.
정렬, 집계, 필드를 대상으로한 작업에선 텀을 보고 문서를 찾는게 아니라 문서를 보고 필드 내의 텀을 찾는다. (그래?..)
엘라스틱 서치에선 text를 제외한 모든 타입에서 이 캐시 방법을 지원한다. ,솔직히 뭔소린지 모르겠다. 집계를 알아야 이해할 수 있을 것 같다!
ㅎ
텍스트는 doc_values 사용할 수 없다. 대신, fielddata 라는 캐싱 방법을 이용한다.
이 필드데이터를 이용하여 집계, 정렬 등을 수행할 때는 역색인 전체를 읽어들여 힙 메모리에 올린다. 애는 oom 발생할 수 있기 때문에 조심조심
_source 필드.
문서 색인 시점에, 원본 JSON 문서를 저장하는 메타 데이터 필드.
조회 API 가 날라온 후, 검색을 완료하여 반환할 문서를 확정하고 나면 이 _source 필드를 읽어 반환한다.
_source 필드 자체는 역색인을 생성하지 않기 때문에 검색 대상이 되지 않는다.
_source 필드는 JSON 문서를 통째로 담기 때문에 디스크 용량을 많이 차지한다. 따라서, 비활성화 시킬 수도 있긴 하지만 추천하진 않는다.
일단, 업데이트 작업을 수행할 수 없다. (엘라스틱 서치에서의 세그먼트는 불변한다. 따라서, update는 사실 문서를 삭제한 후 재생성함으로써 이뤄진다.)
근데, 이 재생성할 때 문서를 _source 필드를 보고 읽어서 업데이트 치는거기 때문에.. 이 필드가 없으면 업데이트가 안된다.
Reindex도 사용할 수 없다.
차라리, 인덳 ㅡ코덱을 바꿔 압축률을 높이는 편이 낫다. ㅎㅎㅋ
index 속성은 해당 필드의 역색인을 만들지를 지정한다. 기본은 true.
false로 하면 역색인이 생성되지 않기 때문에 검색 대상이 되지 않는다. 그래도, 다른 필드를 대상으로 한 검색 쿼리에 문석 ㅏ검색된다면 이 필드도 검색 결과에 포함된다.
또한, doc_value 등을 사용한 집계/ 정렬 등에도 사용은 가능함.
애널라이저는
0개 이상의 캐릭터 필터, 1개의 토크나이저, 0개 이상의 토큰 필터로 이루어진다.
캐릭터 필터엔 html_script 등이 사용된다. <p> Hi <b> World </b> !! </p> 와 같은 텍스트가 들어오면 HTML Tag를 제외한, hi world !! 로 바꿔준다.
그 외에도 문자열만 살리는 letter 등도 적용이 가능하다.
토크나이저에도 여러 종류가 있다. Standard는 단순히 문자열을 텀으로 쪼개주는 것. ngram 같은 것도 있다.
ngram 에서 min, max 값을 지정하면 해당 문자열의 크기로 문자열을 쪼개준다.
토큰 필터는 전부 다 대문자 또는 소문자 또는 치환 등등의 기능을 하는 필터를 넣을 수 있다.
POST _analyze
{
“tokenizer”: {
“type”: “ngram”
“min_gram”: 3
“max_gram”: 4
“token_chars”: [“letter”
},
“text” : “Hello, World!”
}
위의 결과는 hel, hell, ell, ello, llo, wor, worl, orl, orld, rld 다.
왜 ‘owo’ 는 안나올까? token_chars가 있기 때문이다.
위 쿼리를 수행하면, 일단 token_chars에 명시되어있지 않은 기호를 구분으로 문자열을 쪼개 term으로 만든다. 즉, 위에선 hello / world 로 텀이 나뉜다.
그리고 각각의 단어에 대하여 min~max 의 기준으로 단어가 잘린다.
파일: