엘라스틱서치 바이블 - 3장 인덱스 설계
[ 인덱스 설정 ]
GET [index_name]/_settings
인덱스 설정은 인덱스명 뒤에 _settings를 넣어 GET 메서드로 호출한다.
예제
PUT /my_index
{
"settings": {
"number_of_shards": 2,
"number_of_replicas": 2
}
}
>>>
{
"acknowledged": true,
"shards_acknowledged": true,
"index": "my_index"
}
GET my_index
>>>
{
"my_index": {
"aliases": {},
"mappings": {},
"settings": {
"index": {
"routing": {
"allocation": {
"include": {
"_tier_preference": "data_content"
}
}
},
"number_of_shards": "2",
"provided_name": "my_index",
"creation_date": "1745115115705",
"number_of_replicas": "2",
"uuid": "3XaOBSOKTmi-PfcTLvPjiw",
"version": {
"created": "8521000"
}
}
}
}
}
number_of_shards
- 인덱스 데이터를 몇 개의 샤드로 쪼갤 것인지 지정하는 값
- 한번 지정하면 바꾸기가 쉽지 않음
- 샤드 당 루씬 인덱스가 하나씩 더 생성되어 너무 크게 설정하면 클러스터 성능이 떨어짐
- 너무 적게 설정하면 샤드 크기가 커져 복구시간이 오래 걸리고 안정성이 떨어짐
- 기본 값은 1
number_of_replicas
- 주 샤드 하나당 본제본 샤드를 몇 개 생성할 것인지 지정하는 값
- 인덱스 생성 후에도 동적으로 변경 가능
refresh_interval
- 엘라스틱서치가 인덱스 대상으로 refresh를 얼마나 자주 수행할 것인지 지정하는 값
- 기본 값은 "1s", 1초
[ 맵핑 필드와 타입 ]
동적 맵핑
아무 내용이 없던 mappings 항목에 필드의 타입과 정보가 추가된 것을 확인할 수 있다.
인덱스에 문서가 색인 될 때, 기존에 매핑 정보가 없으면 자동으로 적당한 필드 타입을 지정하며 생성한다.
POST /my_index/_doc/1
{
"title": "hellow!",
"views": 1234,
"public": true,
"point": 4.5,
"created": "2025-04-20T14:00:54.123Z"
}
>>>
{
"my_index": {
"aliases": {},
"mappings": {
"properties": {
"created": {
"type": "date"
},
"point": {
"type": "float"
},
"public": {
"type": "boolean"
},
"title": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"views": {
"type": "long"
}
}
},
...
}
명시적 맵핑
맵핑 설정은 한 번 지정이되면 변경하기가 힘들다.
운영시 명시적 맵핑을 활용해야하며, 신규 필드 추가시에도 명시적 맵핑을 이용하는 것이 좋다.
PUT /my_index
{
"mappings": {
"properties": {
"title": {
"type": "text"
},
"views": {
"type": "integer"
},
"public": {
"type": "boolean"
},
"point": {
"type": "float"
},
"created": {
"type": "date",
"format": "strict_date_time"
// ISO 8601, 예: 2025-04-20T14:00:54.123Z
}
}
}
}
# 적용확인
GET my_index/_mapping
필드타입
필드타입은 boolean, text, keyword, long, integer, short, byte, double, float, half_float, scaled_float,
date, ip, array, object, nested, geo_point, geo_shape 등이 있으며
작은 비트를 사용하는 자료형은 색인과 검색시 이득이 있다.
다만 저장할 때는 실제 값에 맞춰 최적화되기에 디스크 사용량에는 이득이 없다.
text 타입과 keyword 타입
text :
- 애널라이저가 적용된 후 색인된다.
- 즉 들어온 값은 분석하여 여러 토큰으로 쪼개어 역색인을 구성한다.
- 전문검색에 적합합니다.
keywork :
- 문자열 값을 여러 토큰으로 쪼개지 않고 역색인한다.
- 간단한 전처리만을 실행하는 노멀라이저를 적용한다.
- 정렬과 집계과 작은 작업일 때 적절하다.
타입 참조 :
https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/field-data-types
[ 애널라이저와 토크나이저 ]
애널라이저는 3단계로 이루어져있다.
캐릭터 필터 (Character Filter)
- 원본 텍스트에서 특수 문자 제거 등의 전처리
- 0개 이상의 캐릭터필터를 지정할 수 있으며 순서대로 수행된다.
토크나이저 (Tokenizer)
- 텍스트를 개별 토큰으로 분리
- 한 개의 토크나이저만 지정할 수 있다.
토큰 필터 (Token Filter)
- 토큰 변형, 추가 또는 제거
- lowercase / pattern_replace / trim / tuncate 등이 있다.
예시
curl -X GET "localhost:9200/_analyze" -H 'Content-Type: application/json' -d '
{
"char_filter": ["html_strip"],
"tokenizer": "standard",
"filter": ["uppercase"],
"text": "<p>hellow, hi word!</p>"
}'
>>>
{
"tokens": [
{
"token": "HELLOW",
"start_offset": 0,
"end_offset": 6,
"type": "<ALPHANUM>",
"position": 0
},
{
"token": "HI",
"start_offset": 8,
"end_offset": 10,
"type": "<ALPHANUM>",
"position": 1
},
{
"token": "WORD",
"start_offset": 11,
"end_offset": 15,
"type": "<ALPHANUM>",
"position": 2
}
]
}
[ 템플릿 ]
인덱스를 설정할 때마다 이런 설정값을 매번지정하는 번거로움이 있기에
사전에 템플릿을 정의해 두면 반복적인 작업을 줄여준다.
인덱스 템플릿
인덱스 패턴에는 * 와일드카드를 사용할 수 있다.
priority 값을 이용하면 인덱스 템플릿 간 우선 적용순위를 조정한다. (높을수록 우선순위 높음)
예시
PUT /_template/my_template
{
"index_patterns": ["test-te*", "bar*"],
"priority": 1,
"template" {
"settings": {
"number_of_shards": 1
},
"mappings": {
"properties": {
"host_name": {
"type": "keyword"
},
"created_at": {
"type": "date",
"format": "EEE MMM dd HH:mm:ss Z yyyy"
}
}
}
}
}
# 생성된 템플릿 조회
GET /_template/my_template
# 위 템플릿에 맞는 인덱스 생성
PUT test-text-1
GET test-text-1
컴포넌트 템플릿
인덱스 템플릿을 많이 만들면 중복되는 부분이 생김나.
이를 재사용할 수 있는 작은 템플릿 블록으로 쪼갠 것이 컴포넌트 템플릿이다.
예제
PUT /_component_template/log_mappings
{
"template": {
"mappings": {
"properties": {
"host_name": {
"type": "keyword"
},
"created_at": {
"type": "date",
"format": "EEE MMM dd HH:mm:ss Z yyyy"
},
"message": {
"type": "text"
},
"level": {
"type": "keyword"
}
}
}
}
}
PUT /_index_template/logs_template
{
"index_patterns": ["logs-*", "app-logs-*"],
"composed_of": ["log_mappings"],
"priority": 100,
"template": {
"settings": {
"number_of_shards": 1,
"number_of_replicas": 1
}
}
}
PUT /_component_template/basic_settings
{
"template": {
"settings": {
"number_of_shards": 2,
"number_of_replicas": 1
}
}
}
PUT /_index_template/complete_logs_template
{
"index_patterns": ["logs-*"],
"composed_of": ["basic_settings", "log_mappings"],
"priority": 200
}
동적 템플릿
동적 템플릿은 인덱스에 새로 들어온 필드의 매핑을 사전에 정의한대로 동적 생성한다.
예제
- description_text와 같이 _text로 끝나는 문자열 필드는 text 타입으로 매핑
- user_id_keyword와 같이 _keyword로 끝나는 문자열 필드는 keyword 타입으로 매핑
PUT my-index
{
"mappings": {
"dynamic_templates": [
{
"text_fields": {
"match_mapping_type": "string",
"match": "*_text",
"mapping": {
"type": "text"
}
}
},
{
"keyword_fields": {
"match_mapping_type": "string",
"match": "*_keyword",
"mapping": {
"type": "keyword"
}
}
}
]
}
}
[ 라우팅 ]
엘라스틱서치가 인덱스를 구성하는 샤드 중, 몇 번 샤드를 대상으로 작업을 수행하지 지정하기 위해 사용하는 값
데이터 분산과 검색 성능에 중요한 영향을 미침
예제
- 5개의 샤드를 가진 익덱스를 생성
- routing 값으로 라우팅을 지정
PUT my-index
{
"settings":{
"number_of_shards" 5,
"number_of_replicas": 1
}
}
PUT my-index/_doc/1?routing=user1
{
"title": "This is a document"
}
조회
GET my-index/_search
{
"took": 5,
"timed_out": false,
"_shards": {
"total": 2, // 실제 검색이 수행된 샤드 수 (user1, user2에 해당하는 샤드만)
"successful": 2,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 42,
"relation": "eq"
},
"max_score": 1.0,
"hits": [
{
"_index": "my-index",
"_id": "1",
"_score": 1.0,
"_routing": "user1", // 이 문서는 user1 라우팅 값으로 저장됨
"_source": {
"title": "Document for User 1",
"content": "This is content for user1",
"timestamp": "2023-07-15T10:30:00Z",
"user_id": "user1"
}
},
{
"_index": "my-index",
"_id": "3",
"_score": 1.0,
"_routing": "user2", // 이 문서는 user2 라우팅 값으로 저장됨
"_source": {
"title": "Document for User 2",
"content": "This is content for user2",
"timestamp": "2023-07-14T14:20:00Z",
"user_id": "user2"
}
},
...
]
}
}
운영에서는 라우팅을 지정하는 것을 권장
- 라우팅 값을 필수로 설정하는 예제
PUT my-index
{
"mappings": {
"_routing": {
"required": true
}
}
}