Backend/Elastic Search

[Elastic Search] 시스템 구조 및 데이터 처리

anxi 2024. 5. 17. 00:07

근황...

요즘 블로그 쓰는게 뜸하죠... 

제가 이번주부터 회사 인턴으로 백엔드 개발자로 근무하게 되었습니다 !!!!!

많이 부족한 실력이지만 개인 공부도 열심히 하고 회사 일도 꼼꼼하게 해서 3개월 뒤에 좋은 개발자가 되었음 좋겠습니다~!

 

P.S 회사에서 사용하는 제 영어 이름은 Koi (코이) 입니다. 책에서 읽고 저의 모토랄까나.. 가치관과 가장 잘 맞는다고 생각해서 지었습니당

https://www.onday.or.kr/wp/?p=13410 (고른 이유가 궁금하시다면 이 글을 읽어보세요!)

 

회사에는 좋은 개발자들과 제가 성장할 수 있는 환경을 충분히 제공해주신 만큼 열심히 노력해서 코이처럼 대인이 되었음 좋겠습니다.

또한 성장에 그치지 않고 주변의 개발을 꿈꾸는 분들에게 좋은 환경을 제공해서 또 다른 코이가 생길 수 있음 좋겠습니다.

 

앞으로 열심히 해보겠습니다!


회사에서 Elastic Search(ES)를 사용하는데 ES에 대해 지식이 전무해서 공부하고 기록하려고 합니다.

 

https://esbook.kimjmin.net/

 

Elastic 가이드 북 | Elastic 가이드북

이 가이드북은 출판을 위해 집필중이던 내용을 Elastic을 처음 시작하시는 분들께 도움이 되고 커뮤니티와 함께 완성 해 나가려는 목적으로 공개하게 되었습니다. 모든 문서에 대한 저작권은 저

esbook.kimjmin.net

이 가이드북을 보고 공부했습니다! 


3. ES 시스템 구조

3.1 클러스터 구성

클러스터 및 노드

  • ES는 대용량 데이터 증가에 따른 스케일 아웃과 데이터 무결성을 유지하기 위한 클러스터링을 지원한다.
  • 항상 클러스터를 기본으로 동작하며 1개의 노드만 있어도 클러스터로 구성된다.
  • 클라스터를 서버라 생각하고, 클라스터 안에 노드가 구성되어있다고 생각하면 된다.
  • 노드 ↔ 클라이언트 통신을 위한 http 포트 (9200~9299)
  • 노드 ↔ 노드 데이터 교환을 위한 tcp 포트 (9300~9399)

총 2개의 네트워크 통신을 열어둔다. 일반적으로 1개의 물리 서버마다 하나의 노드를 실행하는 것을 권장한다.

하지만 물리적인 서버 안에서 여러 개 노드 실행 또한 가능하다.

 

디스커버리

노드가 처음 실행 될 때 같은 서버, 또는 discovery.seed_hosts: [ ] 에 설정된 네트워크 상의 다른 노드들을 찾아 하나의 클러스터로 바인딩 하는 과정이다.

 

디스커버리 작동 순서


3.2 인덱스와 샤드

  • 도큐먼트(document) : 단일 데이터 단위
  • 인덱스(Index) : 도큐먼트를 모은 집합  # 혼동 주의 : 데이터 저장 단위인 인덱스는 인디시즈(indices)라 부른다.
  • 색인 : 데이터를 ES에 저장하는 행위

인덱스는 기본적으로 샤드(shard)라는 단위로 분리 및 각 노드에 분산되어 저장된다.

  • 샤드 : 루씬의 단일 검색 인스턴스

Node, Shard

Document vs Shard

- Document는 ES에 저장되는 개별 데이터 항목으로써 Document를 인덱스에 저장한다.
- Shard는 ES 클러스터에서 데이터를 분산 저장하는 단위로써 인덱스를 하나 이상의 Shard로 분할시켜 확장성과 성능을 향상시킨다.

 

  • 프라이머리 샤드와 복제본(Replica)
    • 클라스터에 노드를 추가하게 되면 샤드들이 각 노드들로 분산되고 디폴트로 1개의 복제본을 생성한다.
    • 프라이머리 샤드 : 처음 생성된 샤드
    • 리플리카 : 복제본  
    • 당연한 말이지만 노드가 1개 있으면 리플리카는 존재하지 않는다.
    • 프라이머리 샤드가 유실될 경우 리플리카가 프라이머리 샤드로 승격되며 다른 노드에 새로운 리플리카가 생성된다.
    • 같은 "샤드와 리플리카는 동일한 데이터를 담고 있어 반드시 서로 다른 노드에 저장"된다.
      • 데이터 가용성과 무결성을 보장한다.

Primary Shard & Replica


3.3 마스터 노드와 데이터 노드

마스터 노드 (Master Node)

  • ES 클러스터는 하나 이상의 노드로 구성되어 있다.
  • 이 중 하나의 노드는 인덱스의 메타 데이터, 샤드의 위치와 같은 클러스터 상태 정보를 관리하는 마스터 노드의 역할을 수행한다.

데이터 노드 (Data Node)

  • 실제로 색인된(저장된) 데이터를 저장하고 있는 노드
  • 데이터 처리에만 집중한다.

Split Brain

  • 마스터 후보 노드를 하나만 놓게 되면 유실 되었을 때 클러스터 전체가 작동을 정지할 위험이 생긴다.
  • 그래서 최소한의 백업용 마스터 노드를 설정하게 되는데 이때 마스터 후보 노드들을 3개 이상의 홀수 개를 놓는 것을 권장한다.
    • 왜 3개 이상 둬야 할까?
      • 만약 짝수개일 경우, 마스터 후보 노드가 분리되면 각자가 서로 다른 클러스터에서 존재하며 동작하기 때문에 추후에 하나의 클러스터로 다시 합쳐지면 데이터 정합성에 문제가 생기고 데이터 무결성이 유지될 수 없다.
  • 결론 : Split Brain 문제를 피하고 싶으면
    • 마스터 후보 노드 개수는 항상 홀수
    • 가동을 위한 최소 마스터 후보 노드 설정은 (전체 마스터 후보 노드) / 2 + 1 로 설정

4. ES 데이터 처리

4.1 REST API

ES는 http 프로토콜로 접근이 가능한 REST API를 지원한다.

 

4.2 CRUD

구조

PUT/POST my_index/_doc
{
  "name":"Koi",
  "message":"안녕하세요 Koi"
}

GET/DELETE my_index/_doc/1

행위를 나타내는 메서드명으로 시작한다.

[PUT/POST] 접근할 인덱스를 지정한 후 Json을 통해 데이터를 입력한다.

[GET/DELETE] 접근할 도큐먼트 id를 지정해서 조회, 삭제한다.

 

입력 (PUT) ↔ 수정 (POST)

 

보통 삽입할 때 POST를 사용하지만 ES에선 입력이 PUT이고 수정이 POST이다.

 

4.3 벌크 연산

  • 여러 명령을 배치로 수행하기 위해선 _bulk API를 사용한다.
  • _bulk API로 index, create, update, delete의 동작을 할 수 있다. 
  • delete를 제외하고는 명령문과 데이터문을 한 줄씩 순서대로 입력하고 delete는 내용 입력이 필요 없기 때문에 명령문만 존재한다.
POST _bulk
{"index":{"_index":"test", "_id":"1"}}
{"field":"value one"}
{"index":{"_index":"test", "_id":"2"}}
{"field":"value two"}
{"delete":{"_index":"test", "_id":"2"}}
{"create":{"_index":"test", "_id":"3"}}
{"field":"value three"}
{"update":{"_index":"test", "_id":"1"}}
{"doc":{"field":"value two"}}
  • 저장 과정
    • test/_doc/1 -> field : value one 을 입력한다.
    • test/_doc/2 -> field : value two 을 입력한다.
    • test/_doc/2 -> delete 삭제한다.
    • test/_doc/3 -> field : value three 을 입력한다.
    • test/_doc/1 -> field : value two 으로 수정한다.

bulk 연산을 사용하는 것이 따로따로 저장하는 것보다 훨씬 빠르기때문에 대용량의 데이터를 저장할 때는 벌크 동작을 고려해보자!


4.4 검색 API - _search API

 

  • 전체 도큐먼트 조회(match_all) 
GET <인덱스명>/_search
  • 쿼리를 통한 검색
GET <인덱스명>/_search?q=countryCode:KR

 

+ AND, OR, NOT 연산도 가능하다.

// AND
GET /<index명>/_search?q=id:KR AND id:US 

// OR
GET /<index명>/_search?q=id:KR OR id:US

 

데이터 본문 (Data Body) 검색

  • 데이터 본문 검색은 검색 쿼리를 데이터 본문으로 입력하는 방식이다.
  • ES의 QueryDSL을 사용하며 쿼리 또한 json 형식으로 되어있다.
GET /test/_search
{
  "query": {
    "match": {
      "id": "US"
    }
  }
}
이렇게 data body에 넣어서 쿼리 검색을 할 수 있다.

 

항상 “query” 지정자로 시작

 

멀티테넌시 (Multitenancy)

  • ES는 여러 개의 인덱스를 한꺼번에 묶어서 검색할 수 있는 멀티테넌시를 지원한다.
  • logs-2024-01, logs-2024-02 와 같이 날짜별로 저장된 인덱스들이 있다면 이 인덱스들을 모두 logs-*/_search 명령으로 한꺼번에 검색할 수 있다.
  • 또는 logs-2024-01, logs-2024-02와 같이 쉼표로 나열할 수도 있다.
1. 쉼표로 구분
GET logs-2024-01,logs-2024-02/_search

2. 와일드카드 *로 구분
GET logs-2024-*/_search