본문 바로가기

분석/데이터베이스

[Elasticsearch] 내부 동작 과정에 대해 알아보자

Elasticsearch 개념

Elasticsearch란?

elasticsearch는 오픈소스 검색엔진인 apache lucene을 기반으로 동작하며

restful 인터페이스를 통해 검색 서비스를 제공한다.

 

인덱스

모든 데이터는 JSON 도큐먼트로 저장되는데 도큐먼트들의 논리적 집합을 인덱스라고 한다.

 

인덱스를 조회해보면 mappings, settings, aliases로 구성된다.

mappings는 데이터가 어떻게 저장되고 검색될지에 관한 스키마이고

settings는 데이터가 어떻게 물리적으로 저장되고 운영할지에 관한 설정이고

aliases는 인덱스의 별칭이다.

GET movies
{
  "alias": {},
  "mappings": {
    "properties": {
      "make": {
        "title": "text"
      }
    }
  },
  "settings": {
    "index": {
      "number_of_replicas": 3
    }
  }
}

 

역인덱스

단어마다 해당 단어가 포함된 문서 목록을 저장한 자료구조로 검색을 빠르게 해준다.

 

도큐먼트의 단어 필드는 불용어가 포함된 경우 필터링되고 토큰화 되어서 

단어마다 빈도수와 단어가 포한된 문서 아이디를 저장하게 된다.

 

매핑

elasticsearch는 데이터를 JSON 도큐먼트로 모델링하고 인덱싱한다.

매핑은 도큐먼트와 그 안의 필드들이 어떻게 저장되고 검색될지를 정하는 설정이다.

 

매핑은 2가지 방식이 있다.

동적 매핑은 데이터를 처음 입력할 때 elasticsearch가 데이터의 형태를 보고 자동으로 타입을 추론해서 매핑을 생성한다.  

의도치 않은 타입으로 지정될 수 있기 때문에 운영환경에서 동적 매핑을 사용하지 않는 것이 권장된다.

정적 매핑은 사용자가 인덱스의 구조와 타입을 정의해서 매핑을 생성한다. 

인덱스 생성 API를 사용하거나 매핑 API를 사용할 수 있다.

# 인덱스 생성 API를 사용해서 인덱스 생성
PUT movies
{
  "mappings": {
    "properties": {
      "title": {
        "type": "text"
      }
    }
  }
}
# 매핑 API를 사용해서 인덱스 업데이트
PUT movies/_mapping
{
  "properties": {
    "release_date": {
      "type": "date",
      "format": "dd-mm-yyyy"
    }
  }
}

 

 

인덱스는 한번 생성되면 기존 필드에 대한 수정이 불가하다.

다른 데이터 타입으로 변경하려면 다음 과정을 거쳐야한다.

1. 데이터 타입을 수정해서 새로운 인덱스를 생성한다.

2. reindexing API를 사용해서 이전 인덱스의 데이터를 새로운 인덱스로 복사한다.

 

아키텍처

노드

클러스터는 여러 노드로 구성되고

노드는 master 노드, data 노드로 분류된다.

노드의 역할은 elasticsearch.yml에서 node.roles로 정할 수 있다.

 

master 노드는 클러스터를 관리하는 작업을 한다.

data 노드는 도큐먼트 관련 작업을 수행한다.

 

샤드

샤드는 물리적인 저장소로 primary 샤드, replica 샤드로 분류된다.

 

replica 샤드는 primary 샤드의 복제본으로

고가용성을 위해 primary 샤드와 다른 node에 배치하는 것이 권장된다.

replica 샤드를 통해 읽기 요청을 분산시킬 수 있고

primary 샤드가 속한 노드가 down될 경우 replica 샤드를 대신 사용할 수 있다.

 

클러스터는 RED, YELLOW, GREEN 상태를 가질 수 있다.

RED는 샤드가 할당되지 않은 상태이고

YELLOW는 샤드가 할당되어있으나 복제본이 할당되지 않은 상태이고

GREEN은 샤드와 복제본이 할당되고 정상 동작중인 상태이다.

인덱스

인덱스 분할과 축소

인덱스에 데이터가 과부하되는 경우 검색 쿼리가 느려지거나 데이터 손실 위험이 생길 수 있다.

movies 인덱스가 5개의 샤드에 걸쳐서 저장되어있는데 과부하가 생긴다면

10개의 샤드를 가진 새로운 인덱스로 확장할 수 있는데 이를 인덱스 분할이라고 한다.

 

인덱스 분할을 하기위해서는 인덱스를 읽기 전용으로 변경하고 인덱스 분할 작업을 한다.

아래에서는 인덱스가 속한 샤드를 10개로 늘리고 있다.

# 읽기 전용으로 변경
PUT movies/_settings
{
  "settings": {
    "index.blocs.write": "true"
  }
}
# 인덱스 분할 API를 사용해서 인덱스 분할
POST movies/_split/new_movies
{
  "settings: {
    "index.number_of_shards": 10
  }
}

 

인덱스 축소는 인덱스 분할과 반대 작업으로

더 적은 수의 샤드를 가진 새로운 인덱스로 축소하는 것을 가리킨다.

 

인덱스 축소를 하기 위해서 인덱스를 읽기 전용으로 변경하고

하나의 노드로 모아서 인덱스 축소 작업을 하고 다시 여러 노드로 재분산한다.

아래에서는 node-1이라는 노드로 인덱스를 모아서

해당 인덱스 속한 샤드를 5개로 축소한 뒤 다시 여러 노드로 재분산하고 있다.

# 읽기 전용으로 변경하고 여러 샤드를 하나의 노드로 모은다
PUT movies/_settings
{
  "settings": {
    "index.blocs.write": "true",
    "index.routing.allocation.require._name": "node-1",
  }
}
# 인덱스 축소 API 사용
POST movies/_shrink/new_movies
{
  "settings": {
    "index.number_of_shards": 5
  }
}
# 재분산
PUT new_movies/_settings
{
  "settings": {
    "index.blocks.write": null,
    "index.routing.allocation.require._name": null
  }
}

 

인덱스 수명 주기

인덱스는 다음과 같은 수명 주기 단계가 있다.

hot : 운영 모드로 인덱스의 읽기 작업과 쓰기 작업이 가능하다.

warm : 인덱스는 읽기 전용이고 비번한 쿼리를 허용한다.

cold : 인덱스는 읽기 전용이고 느린 쿼리를 허용한다.

frozen : 인덱스는 읽기 전용이고 매우 느린 쿼리를 허용한다.

delete : 인덱스가 삭제된다. 삭제 되기전에 shapshot으로 남기기 때문에 복원이 가능하다.

 

ILM은 인덱스 수명 관리로 수명 주기 정책을 관리하는 것을 말한다.

인덱스가 계속 커질 경우 정책을 정의해서 조건을 만족하면 새로운 인덱스로 rollover하도록 할 수 있다.

 

수명 주기 정책을 정의하고 인덱스에 연결한다.

아래에서는 min_age를 0ms로 두어서 바로 hot 단계에 진입하도록 했고

max_age, max_docs, max_size 조건을 두어서 하나라도 만족하면 rollover하도록 하고 있다.

# 인덱스 수명 주기 정책 정의
PUT _ilm/policy/movies_hot_policy
{
  "policy": {
    "phases": {
      "hot": {
        "min_age": "0ms",
        "actions": {
          "rollover": {
            "max_age": "1d",
            "max_docs": 1000,
            "max_size": "10gb"
          }
        }
      }
    }
  }
}
# 인덱스에 인덱스 생명 주기 정책 연결
PUT movies-0001
{
  "settings": {
    "index.lifecycle.name": "movies_hot_policy"
  }
}

 

인덱스를 rollover 하기위해서 접미사에 숫자를 두어야한다. 

rollover를 하면 접미사의 숫자가 증가하면서 인덱스가 생성된다.

movies-0001 movies-0002 ...

이때 별칭을 정의해서 rollover되는 인덱스를 하나의 이름으로 참조할 수 있다.

PUT movies-0001
{
  "alias": {
    "movies-alias": {
      "is_write_index": true 
    }
  }
}