Search
moon
sun

인덱스 설계

목차

1. 인덱스 설정

인덱스 설정

GET [인덱스 이름]/_settings
Bash
복사
•
number_of_shard
◦
인덱스가 데이터를 몇 개의 샤드로 분리할 지 지정
◦
reindex를 하지 않는한 바꿀 수 없다.
•
number_of_replicar
◦
샤드 하나당 복제본 샤드의 갯수 지정
◦
변경이 가능하다.
PUT [인덱스 이름]/_settings { [변경 사항] }
Bash
복사
PUT my_index/_settings { "index.number_of_replicas":0 }
Bash
복사

refresh_interval

•
ES refresh 수행 주기
•
-1로 지정하면 주기적으로 수행하지 않는다.
•
null로 업데이트해서 설정 전으로 되돌릴 수 있다.
PUT my_index/_settings { "index.refresh_interval": "1s" # 1초마다 refresh를 하겠다는 의미 }
Bash
복사

index.search.idle.after

•
마지막 쿼리 수행 이후 해당 설정 시간이 지나면 새로운 쿼리 요청이 오기전까지 refresh를 수행하지 않는다.

인덱스 생성

•
설정값 없이 인덱스를 생성하면 모두 기본값으로 지정되는데, 프로덕션 환경에선 적절하지 않다.
PUT [인덱스 이름] { "settings": { [인덱스 설정] } }
Bash
복사
PUT my_index2 { "settings": { "number_of_shards": 2, "number_of_replicas": 2 } }
Bash
복사
•
number_of_shard와 replicas 정보가 의도한대로 2로 설정되어 있다.
GET my_index2 결과

매핑과 필드 타입

매핑

•
바로 위 GET my_index2 의 결과 중 mappings라는 key가 보인다.
•
매핑(mapping)은 문서가 인덱스에 어떻게 색인되고 저장되는지를 정의하는 부분이다.
•
JSON 문서의 각 필드를 어떤 방식으로 분석하고 색인할지, 어떤 타입으로 저장할지 지정한다.
•
다음 샘플 데이터를 입력 후 mappings를 확인해보자.
PUT my_index2/_doc/1 { "title":"hello world", "views": 1234, "public": true, "point":4.5, "created": "2019-01-17T14:05:01:234Z" } GET my_index2
Bash
복사
: 각 필드에 타입과 관련된 정보가 생성되었다.
ES는 새로운 필드가 들어오면 자동으로 문서의 내용을 보고 타입 추론 후 매핑 정보를 생성한다.
이렇게 ES가 자동으로 생성해주는 매핑을 동적 매핑(dynamic mapping) 이라 부른다.
당연히, 반대로 사용자가 직접 지정하는 방법을 명시적 매핑(explicit mapping)이라 부른다.
"mappings": { "properties": { "created": { "type": "text", "fields": { "keyword": { "type": "keyword", "ignore_above": 256 } } }, "point": { "type": "float" }, "public": { "type": "boolean" }, "title": { "type": "text", "fields": { "keyword": { //멀티 매핑 "type": "keyword", "ignore_above": 256 } } }, "views": { "type": "long" } } }
Bash
복사

운영 환경에서는 명시적 매핑을 사용하자.

•
대부분의 설정은 한 번 지정되면 사실상 변경이 불가능하다.
•
운영 환경에서 대용량 데이터 처리시에는 기본적으로 명시적 매핑을 사용하자.
PUT mapping_test { "mappings": { "properties": { "createdDate": { "type": "date", "format": "strict_date_time || epoch_millis" }, "keywordString" :{ "type": "keyword" }, "textString":{ "type": "text" } } }, "settings": { "number_of_replicas": 1, "number_of_shards": 1 } } # 매핑정보 추가하기 PUT mapping_test/_mapping { "properties":{ "longValue": { "type":"long" } } }
Bash
복사

필드 타입

•
심플 타입
◦
직관적인 데이터 자료형
◦
숫자형(long, integer, short, float, ..)
◦
date형
▪
format이라는 옵션으로 데이터 형식을 지정할 수 있다.
▪
Ex: strict_date_time || epoch_second
▪
날짜 비교 검색시 ES에서는 어떤 형식으로 들어오더라도 내부적으로 UTC 시간대로 변환하여 epoch milliseconds 형식의 long 숫자로 색인한다.
◦
배열은 별도의 타입은 없고 대괄호[ ] 를 이용하면 된다.
•
계층 구조를 지원하는 타입
◦
필드 하위에 다른 필드가 들어가는 계층 구조 데이터 타입
◦
object
▪
JSON 문서는 필드로 또 다른 객체를 가질 수 있다.
▪
계층 구조에 대한 매핑 정보는 계층적으로 나타난다.
put object_test/_doc/1 { "price": 2770.75, "spec": { "cores":12, "memory":128, "storage":8000 } } get object_test ################### "mappings": { "properties": { "price": { "type": "float" }, "spec": { "properties": { "cores": { "type": "long" }, "memory": { "type": "long" }, "storage": { "type": "long" } } } } }
Bash
복사
◦
하위의 계층화된 속성정보는 평탄화된 키-값으로 색인한다.
{ "price": 2770.75, "spec.cores": 12, "spec.memory": 128, "spec.storage": 8000 }
Bash
복사
◦
계층화된 구조가 배열타입일 경우에는 다음과 같이 색인된다.
put object_test/_doc/2 { "spec":[ { "cores":12, "memory":128, "storage":8000 }, { "cores":6, "memory":64, "storage":8000 }, { "cores":6, "memory":32, "storage":4000 } ] } # 다음과 같이 평탄화되어 색인된다. { "spec.cores": [12, 6, 6] "spec.memory": [128, 64, 32] "spec.storage": [8000, 8000, 4000] }
Bash
복사
◦
nested
▪
object 타입과 비슷하지만 배열 내 각 객체를 독립적으로 취급한다.
▪
다른 필드 복합 조건이 object가 or조건에 가깝다면 nested는 and조건에 가깝다.
타입
object
nested
용도
일반적인 계층 구조
배열 내 각 객체를 독립적으로 취급해야 하는 상황
성능
상대적으로 가볍다
상대적으로 무겁다
검색
일반적인 쿼리 사용
전용 nested 쿼리로 감싸서 사용
•
text 타입과 keyword 타입
text 타입과 keyword타입의 역색인 과정
◦
둘 다 문자열 필드 타입이다.
◦
text
▪
애널라이저를 통해 값을 분석 후 토크나이징 한 뒤 역색인이 된다. (term)
▪
전문 검색에 적합하다.
▪
fielddata 캐시를 사용한다.
◦
keyword
▪
노멀라이저를 통해 간단한 전처리 후 전체 키워드를 역색인한다.
▪
일치 검색에 적합하다.
▪
doc_values 캐시를 사용한다.

참고: doc_value와 fielddata

•
doc_values
◦
디스크 기반 자료구조로 파일 시스템 캐시를 통해 정렬,집계,스크립트 작업을 수행할 수 있도록 지원한다.
◦
text, annotion_text를 제외한 대부분의 필드는 doc_values를 지원한다.
◦
정렬등의 작업을 할 일이 없는 필드는 doc_values를 끌 수 있다. (속성을 false로 )
•
fielddata
◦
fielddata는 정렬, 집계, 스크립트 작업 수행 시 역색인 전체를 읽어 힙 메모리에 올리는데, 이는 OOM등의 문제 발생 가능성이 있다. 그렇기에 기본적으로는 비활성화(false) 상태이다.
◦
text는 정렬, 집계, 스크립트 작업이 필요할 경우 fielddata라는 캐시를 이용한다.
◦
어지간해선 활성화시키지 말자

_source

•
문서 색인 시점에 ES에 전달된 원본 JSON 문서를 저장하는 메타데이터 필드
•
_source 필드 자체는 역색인 대상이 아니라 검색 대상이 되진 않는다.
•
비활성화 할 경우 재인덱싱이나 업데이트등을 사용할 수 없으니 활성화 해두자.
•
디스크 공간이 절약해야만 한다면 이 설정을 비활성화 하기보단 인덱스 데이터 압축률을 높히자.
PUT codec_text { "settings": { "index": { "codec": "best_compression" # default(LZ4), best_compression(DEFLATE) } } }
Bash
복사

index

•
해당 필드의 역색인을 만들 것인지 지정할 수 있다.
•
ES 8.1부터는 설정을 false로 설정해도 doc_value를 통해 검색이 수행될 수 있다.
◦
성능은 떨어지지만 디스크 공간을 절약한다.
•
설계 시점에 검색 대상이 될 가능성이 없거나 어쩌다 한번씩만 검색되는 필드는 index를 false로 하는 것도 고려할 수 있다.
put mapping_test/_mapping { "properties": { "notSearchableText": { "type":"text", "index": false }, "docValuesSearchableText": { "type": "keyword", "index": false } } } put mapping_test/_doc/4 { "textString": "Hello, World!", "notSearchableText": "World, Hello!", "docValuesSearchableText": "hello" } get mapping_test/_search { "query": { "match": { "docValuesSearchableText": "hello" } } }
Bash
복사
•
검색 필드를 notSearchableText로 하면 검색되지 않는 걸 볼 수 있는데 notSearchableText는 text 필드이기 때문에 doc_values를 사용하지 않기 때문이다.
•
docValuesSearchableText은 index가 꺼져있어도 doc_values설정을 통해 검색할 수 있었다.

enabled

•
object 타입 필드에만 적용된다.
•
false로 지정된 필드는 ES에서 파싱수행도 하지 않는다. 즉 _source 외에는 저장되는 곳이 없다.
•
역색인이 되지 않았기 때문에 검색도 불가능하고 정렬이나 집계도 안된다.
•
여러 타입이 혼용되는 배열도 enabled가 false면 유효성 체크를 하지 않아 저장할 수 있다.
•
데이터를 _source에서만 확인만 하면 되는 필드가 있다면 false로 설정하는 것을 고려할 수 있다.

애널라이저와 토크나이저

•
text 타입의 데이터는 애널라이저를 통해 여러 텀(term)으로 쪼개져서 색인된다.

애널라이저

•
애널라이저는
◦
0개 이상의 캐릭터 필터
▪
문자열 변형
◦
1개의 토크나이저
▪
변형된 문자열 분리
◦
0개 이상의 토큰 필터로 구성된다.
▪
분리된 토큰에 추가 변형작업 수행
◦
위 과정들을 마치면 분석 완료된 텀(term)이 완성된다.
◦
_analyze 를 통해 어떻게 분석되는지 확인 가능하다.
post _analyze { "analyzer": "standard", "text": "Hello, HELLO, World!" }
Bash
복사

애널라이저 - 캐릭터 필터

•
텍스트를 캐릭터의 스트림으로 받아 데이터를 추가,변경,삭제한다.
•
빌트인 캐릭터 필터는 다음과 같이 존재한다.
◦
HTML strip: <b>와 같은 요소 안쪽 데이터를 꺼내고, HTML 엔티티 디코딩도 한다.
◦
mapping: 치환할 대상 문자와 치환문자를 맵 형태로 보관한다.
◦
pattern replace: 정규 표현식을 이용해 문자를 치환한다.

애널라이저 - 토크나이저

•
1개의 토크나이저만 지정할 수 있다.
•
standard
◦
기본적인 토크나이저로 Unicode Text Segmentation 알고리즘을 사용하여 텍스트를 단어 단위로 나눈다.
◦
대부분의 문장 부호가 사라진다.
•
keyword
◦
들어온 텍스트를 쪼개지않고 그대로 내보낸다.
•
ngram
◦
min_gram 값 이상 max_gram 값 이하 단위로 쪼갠다.
◦
min_gram : 2 max_gram: 3 으로 설정하고 hello를 토크나이징할 경우
▪
he, hel, el, ell, ll, llo로 토크나이징 된다.
◦
무의미한 공백 문자도 token_chars 속성으로 토큰에 포함시킬 타입의 문자 지정 가능
▪
Letter : 언어의 글자로 분류되는 문자
▪
Digit : 숫자로 분류되는 문자
▪
whitespace : 띄어쓰기나 줄바꿈
▪
punchuation : !나 “같은 문장 부호
▪
symbol : $나 ⎇같은 기호
▪
custom : custom_token_chars 설정을 통해 따로 지정한 커스텀 문자
◦
우선적으로 token_chars 지정되지 않은 문자를 기준으로 토크나이징을 한 뒤 속성에 따라 단어를 토크나이징 한다.
◦
RDB의 Like 유사검색과 유사하게 검색을 구현하고 싶을 때 활용한다.
◦
min_gram과 max_gram의 값 차이는 index.max_ngram_diff으로 제한을 설정할 수 있다.
•
edge_ngrma
◦
ngram과 유사하지만 토큰의 시작 글자를 단어의 시작글자로 고정시킨다.
◦
Hello, World를 토크나이징 할 경우 Hel, Hell, Wor, Worl으로 토크나이징된다.

애널라이저 - 토큰 필터

•
토크나이저를 통해 만들어진 토큰 스트림을 받아 토큰에 대한 후처리를 한다. (추가,변경,삭제)
•
내장 토큰은 다음과 같이 존재한다.
◦
lowercase / uppercase : 토큰을 대/소문자로 만든다.
◦
stop: 불용어를 지정해 제거할 수 있다. (ex: the, a, an, in등)
◦
synonym: 유의어 사전 파일을 지정해 지정된 유의어를 치환한다.
◦
pattern_replace: 정규식을 사용해 토큰의 내용을 치환한다.
◦
stemmer : 지원되는 몇몇 언어의 어간 추출 수행(not korean)
◦
trim: 토큰 전후 공백 제거
◦
truncate: 지정한 길이로 토큰을 자른다.

내장 애널라이저

•
필터와 토크나이저를 조합해둔 내장 애널라이저가 몇 가지 존재한다.
•
standard(default)
◦
standard 토크나이저 + lowercase 토큰 필터
•
simple
◦
letter가 아닌 문자 단위로 토크나이징 후 lowercase 토큰 필터 적용
•
whitespace
◦
whitespace 토크나이저로 구성
•
stop
◦
standard 애널라이저 + stop 토큰 필터
•
keyword
◦
keyword 토크나이저로 구성되어 있다.
◦
분석을 하지 않고 하나의 큰 토큰을 그대로 반환한다.
•
pattern
◦
pattern 토크나이저 + lowercase 토큰 필터
•
language
◦
여러 언어의 분석 지원( 한국어는 지원하지 않는다.)
•
fingerprint
◦
standard 토크나이저
◦
+ lowercase 토큰필터
◦
+ ASCII folding 토큰필터
◦
+ stop 토큰필터
◦
+ fingerprint 토큰필터

애널라이저를 매핑에 적용

•
전체적인 default analyzer 설정과, 타입별 analyzer 지정이 가능하다.
put analyzer_test { "settings": { "analysis": { "analyzer": { "default": { "type": <애널라이저 타입> } } } }, "mappings": { "properties": { "defaultText": { "type": "text" }, "standardText": { "type": "text", "analyzer": <애널라이저 타입> } } } }
Bash
복사
•
커스텀 애널라이저를 직접 매핑할수도 있다.
put analyzer_test2 { "settings": { "analysis": { "char_filter": { "my_char_filter": { "type": "mapping", "mappings": [ "i. => 1.", "ii. => 2.", "iii. => 3.", "iv. => 4." ] } }, "analyzer": { "my_analyzer": { "char_filter": [ "my_char_filter" ], "tokenizer": "whitespace", "filter": ["lowercase"] } } } }, "mappings": { "properties": { "myText": { "type":"text", "analyzer":"my_analyzer" } } } }
Bash
복사

참고: 한국어 형태소 분석 애널라이저 플러그인

•
공식적으로 ES에서 한국어 형태소 분석을 지원하진 않지만, nori라는 플러그인을 통해 분석할 수 있다.
•
설치 방법
elasticsearch-plugin install [플러그인 이름] #elasticsearch-plugin install anaysis-nori
Bash
복사
◦
이후 analyzer 속성의 값을 nori로 지정해서 분석하면 된다.

템플릿

•
인덱스 설정, 매핑, 애널라이저같은 설정들을 템플릿화해서 반복작업을 없앨 수 있다.

인덱스 템플릿

PUT _index_template/my_template { "index_patterns": [ "pattern_test_index-*", "another_pattern-*" ], "priority": 1, "template": { "settings" : { "number_of_shards": 2, "number_of_replicas": 2 }, "mappings": { "properties": { "myTextField": { "type": "text" } } } } }
Bash
복사
•
index_pattern
◦
해당 템플릿을 지정할 인덱스의 네이밍 패턴을 지정한다. priority를 통해 여러 템플릿 중 우선순위 조정도 가능하다.

컴포넌트 템플릿

•
인덱스 템플릿에서 중복되는 블록을 쪼개어 재사용할 수 있게 만들어둔 템플릿
PUT _component_template/my_shard_settings { "template": { "settings": { "number_of_shards": 2, "number_of_replicas": 2 } } } PUT _index_template/my_template2 { "index_patterns": ["timestamp_index-*"], "composed_of": ["timestamp_mappings", "my_shard_settings"] }
Bash
복사

라우팅

•
ES에서 인덱스를 구성하는 샤드 중 몇 번 샤드를 대상으로 작업할지 지정하기 위해 사용하는 값
•
작업 대상 샤드 번호는 지정된 라우팅 값을 해시한 후 주 샤드의 개수로 나머지 연산을 수행한 값이 된다.
•
라우팅 값을 지정하지 않으면 _id 값이 기본값이 된다.
•
검색 시(_search) 라우팅 값을 queryString에 작성하면 해당 샤드를 대상으로 검색한다.
put routing_test { "settings": { "number_of_shards":5, "number_of_replicas":1 } } put routing_test/_doc/1?routing=myid { "login_id":"myid", "comment": "hello world", "created_at": "2020-09-08T22:14:09.123Z" } get routing_test/_search?routing=myid
Bash
복사
routing 값 지정 하지 않은 경우
routing 값을 지정한 경우
•
운영 환경에서 라우팅 값을 지정해주는게 성능부분에서 크게 도움이 된다.
◦
Ex: 게시판에서 사용자별 댓글을 색인하고자 할때 사용자의 아이디로 라우팅 값을 지정하면 사용자별 댓글을 조회할 때 검색 성능을 끌어올릴 수 있다.

라우팅 필수 지정 설정

•
라우팅값을 설정하는 것이 선택적(Optional)이 아닌 필수적(required)으로 정책을 통일하고 싶다면 mappings._routing.required 속성 설정을 통해 라우팅 값 명시를 필수로 설정할 수 있다.
PUT routing_test2 { "mappings": { "_routing": { "required": true } } } PUT routing_test2/_doc/1 { "comment": "index without routing" }
Bash
복사
라우팅 값 없이 저장 결과

정리

◦
ES에서 인덱스는 상세설정이 가능한만큼 설정에 주의가 필요하다.
◦
number_of_shard를 통해 인덱스가 몇 개의 샤드로 분리할 지 지정한다.
◦
number_of_replicas를 통해 샤드의 복제본 수를 지정한다.
◦
refresh_interval속성으로 refresh 주기를 정할 수 있다.
◦
인덱스는 자동보단 수동으로 직접 설계해주자
◦
운영 환경에서는 명시적 매핑(explicit mapping)을 사용해주자.
◦
text 타입은 애널라이저를 통해 여러 term으로 쪼개져서 색인된다.
◦
애널라이저는 캐릭터 필터, 토크나이저, 토큰 필터로 구성되어 있다.
◦
한국어는 nori라는 애널라이저 플러그인을 통해 형태소 분석을 할 수 있다.
◦
애널라이저나 인덱스 설정들을 템플릿화 할 수 있다.
◦
템플릿의 중복되는 블록을 분리해 컴포넌트화 할 수 있다.
◦
라우팅 값을 지정해서 검색할 대상 샤드를 좁힐 수 있다.
◦
required속성을 통해 라우팅 지정 정책을 강제할 수 있다.
Kakao Channel