elasticsearch를 사용해야되는 상황이 생겨서 직접 실습하고 사용하기전에 elasticsearch에 대한 전체적인 구조에 대한 간단한 이해와 elasticsearch와 함께 사용되는 툴들이 어떤 것이 있고 어떤 용도로 활용되는지 이해하기 위해 글로 정리하려고 한다.
인덱스(index) VS 역인덱스(inverted index)
Elastic Search는 Lucene의 역인덱스(역색인)을 활용하여 높은 검색 성능을 자랑하는데 대체 역색인이 어떤 구조를 가지고 있고 어떤 역활을 하기에 Elasticserach의 높은 검색 성능에 도움을 주는지 인덱스와 비교하여 알아보려고한다.
인덱스(Index)
인덱스부터 간단하게 설명하자면 추가적인 쓰기 작업과 저장 공간을 활용하여 데이터베이스의 테이블에서 특정 열(column)의 데이터를 색인화한다. 이렇게 색인화된 인덱스는 테이블에 데이터를 조회할 때 전부 스캔하지 않고 조회하려는 데이터의 위치를 가르켜서 빠르게 조회할 수 있도록 도와준다.
역인덱스(Inverted Index)
일반적인 인덱스는 각 키워드와 저장된 주소들을 따로 저장하여 User 테이블의 김철수라는 이름을 가진 사람을 찾으라는 명령어에는 빠른 성능을 보여주지만 김씨로 시작하는 사람을 찾으라는 명령어에는 낮은 성능을 보여준다. 왜냐하면 그냥 인덱스로는 김씨로 시작하는 사람을 찾기위해서 모든 Row의 Name컬럼을 확인해 김으로 시작하는지 안하는지 판단해야하기 때문이다. 이처럼 인덱스의 자료구조의 문제를 해결하기 위해 나온 자료구조가 역인덱스다.
예시
예시로 블로그 테이블이 있다고 가정하고 블로그 제목을 역인덱스를 생성하면 아래와 같은 구조가 될 것이다.
여기서 더 자세히 설명하자면 블로그 테이블에 있는 블로그 제목에 역인덱스를 생성하게 되면 블로그 제목 컬럼에 있는 데이터 중 색인으로 활용할 단어들을 추출하여 아래와 같은 테이블의 형태로 저장하게 된다. 홍대 맛집이라는 검색을 하게 되면 모든 블로그 제목들을 스캔할 필요 없이 역인덱스를 활용하여 홍대와 맛집이라는 키워드를 가지고 있는 Row를 찾으면 된다.
블로그 테이블
blog_id | blog_title |
101 | 홍대 맛집 추천 드립니다. |
102 | 연남동 맛집 추천 드립니다. |
103 | 홍대 카페 추천 드립니다. |
블로그 제목 역인덱스
term | blog_id |
홍대 | 101, 103 |
연남동 | 102 |
맛집 | 101, 102 |
카페 | 103 |
추천 | 101, 102, 103 |
드립니다. | 101, 102, 103 |
Elasticsearh
- 초기의 검색 엔진의 오픈소스는 라이브러리 형태로 이루어지면서 한번 구축하는데 해야할 작업이 많았다.
- 그래서 Lucene(자바로 만들어진 고성능 정보 검색라이브러리)을 프레임워크처럼 구성하여 쉽게 사용할 수 있도록 확장하였다.
- Elasticsearch는 숫자, 날짜, IP, 지리정보 등 다양한 데이터 타입을 다루는데 최적화되어 있다.
- Elasticsearch는 검색엔진이면서 데이터를 저장하는 데이터베이스의 역활을 하기도 한다. Nosql과 매우 유사하기 때문에 Nosql이라고 생각해도 좋다.
- 이론적으로 충분한 크기의 클러스터로 구성되어 있으면 데이터의 양과 무관하게 1초내의 응답을 줄 수 있다.
- Elastic Search의 각 Shard들은 Lucene의 역색인들로 이루어져 있기 때문에 각 샤드들은 Lucene의 인스턴스로 봐도 무방하다.
- Elastic Search는 유사도 스코어를 기반으로 정렬을 제공한다. 다양한 스코어링 방법을 통해 사용자는 입맛에 맞는 알고리즘을 설계할 수 있다.
- 복수의 루씬 인스턴스들을 병렬로 배치하고 분산처리함으로써 무한한 확장을 할 수 있도록 설계되어있다.
- 레플리카 노드를 통해 일부 노드가 다운되더라도 정상적인 운영이 가능하다.
- 가장 흥미로운 점은 모든 통신을 REST API로 이용하도록 만들어서 개발자, 비개발자할 것없이 쉽게 활용이 가능하다.
- 단점은 저장 공간이 크게 압축되지 않고 분산되어 있어 시스템 리소스를 많이 황용한다.
- 인덱스가 불변의 자료구조이기 때문에 변경하려면 삭제하고 새로 생성하여 인덱싱해야되는 불편한점을 가지고 있다.
Kibana
- 엘라스틱 스택의 UI를 담당하며 콘솔, 모니터링 등 여러 시각적인 기능들을 제공한다.
- 키바나에서 가장 중요한 것은 시각화된 대시보드이다. 라인 차트, 파이 차트, 테이블, 지도등을 UI로만 기능을 제공한다.
- 성능 모니터링을 하기 위한 APM 기능과 보안 이벤트를 관제하는 SEIM 등 다양한 기능을 제공한다.
Logstash
- 로그 스태시를 활용하면 로그, 매트릭, 웹 어플리케이션 다양한 로그 수집 가능.
- 비정형 데이터나 반정형 데이터를 분석하기 쉬운 형태로 정제할 수 있고 카프카, 엘라스틱 서치, S3등 다양한 플랫폼에 데이터 전송 가능.
- 다양한 플러그인을 통해 코딩없이 로그를 가공할 수 있다.
- 배치 작업과 병렬 처리를 통해 엘라스틱 서치의 인덱싱 성능을 최적화 할 수 있다.
- 영속성 큐를 통해 중복 처리 발생 방지와 유동적인 처리방식으로 인해 부하 상황에도 안정성을 보장해준다.
- 커스텀 어플리케이션보다 튜닝과 디버깅하기가 쉽다.
Beats
- 로그 스태시는 다양한 기능을 제공하는 것 만큼
X-pack
- Elastic Search과 Kibana에 대한 고급 기능을 제공해주는 패키지
- 보안, 경보, 모니터링, 그래프 분석, 머신 러닝등의 기능을 제공함
Elasticsearh에 대한 이해
Documents
document는 데이터 베이스의 개념으로 보자면 엔티티의 있는 한줄의 행과 같은 개념이라고 볼 수 있다. 텍스트 형태뿐만 아니라 구조화된 데이터의 형태면 전부 저장이 가능하다. 모든 Document는 고유키를 가질 수 있으며 사용자가 직접 설정할 수도 있고 Elastic Search에서 직접 할당 하도록 설정할 수도 있다.
Indices
가장 높은 단계의 엔티티이며 여러 Document를 소유할 수 있다. 데이터 베이스와 비교하자면 Index는 테이블의 개념이라고 생각하고 Document는 테이블의 행이라고 생각하면 이해하기 쉬울 것이다. 각 Index에는 Document의 유형을 정의하는 체계를 가지고 있으며 하나의 Index안에서는 하나의 Document Type만 지정할 수 있다.
정리
기존에 데이터베이스를 이해하고 있다면 클러스터는 데이터 베이스 인덱스는 테이블 Document는 행이라고 이해한다면 Elastic Search가 가지고 있는 전체적인 논리 구조에 대해 이해하기 쉽다. 데이터 베이스의 개념은 비슷하지만 사용법과 작동하는 방식에 대해서는 매우 다르다는 것을 실습해보면 알 수있다.
TF-IDF (Term Frequency x Inverse Document Frequency)
TF(Term Frequency) 는 지정된 검색어가 Document에 나타나는 빈도를 의미한다. 예를 들어 어떤 Document에 코딩이라는 단어를 자주 사용한다면 TF는 높아진다. 마찬가지로 "입니다.", "저것은", "이것은"등의 단어도 많이 사용한다면 TF의 비중이 높아진다. DF는 모든 문서에 사용되는 빈도를 의미한다. 예시로 "내가", "이것은"과 같은 단어는 다른 단어에 비해 DF가 높고 "코딩", "돌고래"등과 같은 단어는 앞에 예시로 든 단어에 비해 DF가 낮을 것이다.
여기서 TF에서 DF를 나눈다면 TF에 IDF(역문서빈도)를 곱하는 것과 같다. 이렇게 나온 알고리즘이 수학적으로 관련성을 구하는데 쓰이는 알고리즘이다. 예시로 채용공고 사이트에 "객체지향 설계 경험"을 검색한다면 "경험"라는 단어는 모든 문서에 자주 사용되는 단어이기 때문에 높은 관련성이 없는 문서들이 검색될 것이고 "객체지향"은 모든 문서에 자주 등장하지 않기 때문에 높은 관련성을 보일 것이다. 이처럼 검색엔진은 위와 같은 구조로 관련성이 높은 순서대로 Document를 가져온다.
Elasticsearch 사용할 수 있는 방법
RestfulAPI
가장 일반적으로 사용할 수 있는 방법 중 하나로 Restful API로 사용할 수 있다. HTTP에서 활용하는 메서드를 활용하여 생성, 수정, 조회등을 할 수 있다. 이러한 사용방법은 Restful API를 사용할 수 있는 환경이라면 쉽게 접근할 수 있다는 장점을 가지고 있다. 이러한 장점을 통해 다양한 도구, 언어, API를 활용하여 사용할 수 있게된다.
예시
인덱스 생성
PUT /my-index-000001
document 생성
PUT /<target>/_doc/<_id>
POST /<target>/_doc/
PUT /<target>/_create/<_id>
POST /<target>/_create/<_id>
Client API
우리가 일라스팅 서치를 활용하여 웹 서버나 웹 어플리케이션을 Restful API를 활용하여 기능을 개발한다고 하면 많은 어려움 생길 것이다. 예로들어 기능을 추가할 때마다 Restful API를 통신하는 로직을 추가해야될 것이고 Json 포맷도 다시 구성해야할 것이다. 또한 인프라스트럭처에 매우 의존하게 되면서 API 형식이 바뀌거나 버전업이 된다면 의존하고 있었던 로직이 모두 수정해야 한다. 다행히도 이러한 문제를 해결하기 위해 엘라스틱 서치와 영동되는 다양한 클라이언트 API가 존재하고 있기 때문에 다양한 클라이언트 API를 활용하면 쉽게 개발이 가능하다.
Analytic tool
elastic 스택에서 web ui 형태로 제공해주는 kibana도 그중 하나이다. 엘라스틱 서치의 인덱스를 ui형태로 관리할 수 있게 도와주며 다양한 그래프를 제공하여 이해하기 쉽게 사용할 수 있다.
엘라스틱 서치의 확장성
인덱스와 샤드
엘라스틱 서치에서는 샤드라는 개념이 존재한다. 이는 하나의 인덱스에 너무 많은 데이터가 쌓여 사용자가 데이터를 요청했을 때 서버에 부하가 걸리는 문제를 해결하기 위해 나온 개념이다. 예를 들어 여러 서버를 관리하는 클러스터가 존재한다면 엘라스틱 서치는 각 샤드를 분산 저장하여 각 서버 인스턴스들에 저장하고 처리하면서 효율적인 분산 처리가 가능하다. 그리고 엘라스틱 서치는 샤드의 아이디 값을 해시하여 관심있는 문서들을 조회할 때 특정한 수학함수를 통해 빠르게 처리를 할 수 있다.
샤드의 문제점
샤드가 분산되면서 데이터 처리를 더 빠르고 효율적으로 사용할 수 있게 되었지만 특정 샤드에 문제가 발생했을 때 샤드가 있는 서버 인스턴스를 복구해야하고 그과정에서 데이터도 손실할 수 있다. 다행히도 엘라스틱 서치는 이러한 문제점을 프라이머리 샤드(원본)와 레플리카 샤드(복제본)라는 개념으로 시스템 장애의 면역을 가지도록 설계되어 있다. 아래의 예시를 통해 좀 더 자세히 알아보자.
노드 1이 시스템에 장애가 정상적인 처리를 할 수 없게되면 어떻게 될까? 가장 먼저 노드 1에 존재하는 프라이머리1 샤드와 레플리카 0샤드가 죽게 될 것이다. 여기서 엘라스틱 서치는 노드2, 노드3에 이미 복제본을 따로 저장해두었기 때문에 복구될 때 까지 큰 문제 없이 사용이 가능하다.
하지만 여기서 더 들어가서 노드1이 아예 죽어버려서 사라진다면 어떤 일이 일어날까? 그런 문제가 발생했을 때 엘라스틱 서치는 노드2나 노드3에 존재하는 레플리카 샤드 하나를 선택해 프라이머리 샤드로 전환하게 된다. 노드1이 복구하는 동안 대체된 프라이머리 샤드와 레플리카 노드가 데이터를 처리하기 때문에 정상적으로 동작할 수 있게 된다. 하지만 여기서 타임아웃이 발생하여 복구되지 못한다면 손실된 샤드 수만큼 새로 생성하여 분산 저장한다.
프라이머리 샤드와 레플리카 샤드의 동작 구조
쓰기 요청
- 클라이언트에 색인 처리할 데이터를 처리해달라고 요청
- 노드1에 저장해야되는 프라이머리 샤드가 맞는지 확인
- 노드1에서 저장해야되는 프라이머리 샤드의 위치를 확인하고 데이터를 저장하고 색인화 한다. (요청하는 Document의 샤드의 위치를 가리키고 있기 때문에 어떤 노드에 전달하더라도 프라이머리 샤드에 저장됨)
- 프라이머리 샤드에 정상적으로 색인화가 되었다면 레플리카 샤드에 데이터를 복제된다.
읽기 요청
- 클라이언트에서 특정 Document 내용을 요청한다.
- 특정 노드에 요청이 전달되면 프라이머리 샤드나 레플리카 샤드에 요청한 Document 내용을 읽어서 전달한다.
이처럼 로드를 효율적으로 분산시키기 때문에 레플리카 노드가 많을수록 전체 클러스터의 읽기 용량은 증가한다. 하지만 프라이머리 샤드가 많을수록 쓰기용량은 제한된다.
샤드 사용 팁
샤드의 복원력을 높이기 위해 홀수의 노드수를 유지
클러스터에는 여러 노드가 있고, 이들 사이에는 데이터의 일관성을 유지하기 위해 항상 통신이 이루어진다. 특히 분산 시스템에서는 분할 허용(failover) 메커니즘, 레플리케이션, 동기화 등을 위해 클러스터 노드 간에 계속적인 정보 교환을 수행하는 구조이다.
여기서 quorum(클러스터의 최소 노드 개수)의 개수를 홀수로 유지하는 것이 매우 중요한데 그이유는 노드간 동시성 문제나 스플릿 브레인이 발생 시킬 수 있기 때문이다. 예시로 아래의 그림처럼 짝수로 노드를 구성했다고 가정해보자.
여기서 x네트워크 파티션에 A, B가 존재하고 Y 네트워크 파티션에 C, D가 존재한다. 갑자기 불특정 장애로 인해 X, Y간의 네트워크통신이 끊겼다고 가정해보자. Y네트워크 파티션에는 마스터 노드가 존재하지 않기 때문에 투표를 통해 마스터 노드를 생성하게 된다. 여기서 문제는 하나의 클러스터만 존재해야되는데 두개의 클러스터가 독립적으로 존재하게 되면서 서로 다른 상태를 가지고 데이터 싱크의 문제를 일으킬 수 있다.
그리고 노드수를 홀수개수로 유지하는 것에 대한 장점은 투표 매커니즘에 의해 결정되는 마스터 노드에 적합하다는 것이다. 예시로 짝수 갯수의 노드가 마스터 노드를 지정하기위해 투표했는데 동일한 투표수가 나온다면 문제를 일으킬 수 있기 때문이다. 퀴럼을 설정할 때 ⌊총 노드 수/2⌋ + 1를 활용하여 사용하면 좋다.
프라이머리 샤드의 수를 수정할 수 없다.
아쉽지만 엘라스틱 처치에서는 프라이머리 샤드의 수를 수정할 수 없다. 인덱스를 생성할 때 부터 프라이머리 샤드의 수를 지정하여 설정해야한다. 많이 불편하다고 생각하겠지만 엘라스틱 서치는 어플리케이션에서 읽기를 중심으로 사용되어지기 때문에 생각보다 나쁘지 않다.
총 샤드 개수 계산법
위의 그림을 보면 인덱스를 생성하면서 프라이머리 샤드수는 3개, 레플리카 샤드는 1개로 지정했다. 총 샤드수는 몇개일까? 정답은 6개 이다. 그 이유를 설명하자면 프라이머리 샤드는 3개로 생성이 되었고 해당 프라이머리 샤드들은 1개의 레플리카 샤드를 가지게 되면서 6개가 생성이 된것이다. 쉽게 설명하면 아래와 같은 형태다.
number_of_shards:3, number_of_replicas: 1
- 프라이머리 샤드 a, 레플리카 샤드a
- 프라이머리 샤드 b, 레플리카 샤드b
- 프라이머리 샤드 c, 레플리카 샤드c
- 총 6개
number_of_shards:3, number_of_replicas: 2
- 프라이머리 샤드 a, 레플리카 샤드a1, 레플리카 샤드a2
- 프라이머리 샤드 b, 레플리카 샤드b1, 레플리카 샤드b2
- 프라이머리 샤드 c, 레플리카 샤드c1, 레플리카 샤드c2
- 총 9개
정리 후기
엘라스틱 서치에 대한 개념을 정리하면서 엘라스틱 서치가 어떤 문제를 해결하기 위해 나온 기술이고 어떤 구조로 설계되어 있는지 대략이나마 알 수 있었다. 그리고 느꼈던 점은 크게 두가지가 있었는데 첫번째는 내가 생각했던 것보다 많은 곳에 활용할 수 있을 것 같다는 것. 두번째는 왜 검색엔진만을 위한 팀이 따로 있었던 것인지 알 수 있었다. 엘라스틱 서치을 활용하기 위해서는 인프라의 사양, 클러스터의 인프라 구조, 각 클러스터 노드간의 네트워크 파티션 분배와 각 인덱스의 적정 샤드수 등을 도입하는데에만 정말 많은 것을 고려해야하고 더 많은 트래픽과 데이터 처리를 요구할수록 관리하기 쉽지 않을 것 같다는 생각이 들었다. 하지만 많은 리소스가 필요한 만큼 다양한 활용을 할 수 있는 것과 엘라스틱 스택에서 제공하는 기능들의 대체재도 적고 제공하는 기능들도 제한적이기 때문에 필요한 상황이 된다면 무조건 활용해 볼 것 같다.
'Elasticsearch' 카테고리의 다른 글
Logstash Architecture(로그스태시 아키텍처) (0) | 2023.08.27 |
---|---|
ELK 정복하기 - 애널라이저(분석기) (0) | 2023.08.20 |
Elasticsearch7 이상부터 사용해야하는 이유 (0) | 2023.07.17 |
Elastic search 설치(Mac, Docker) (0) | 2023.07.15 |
ES1 IndexCreationException 트러블 슈팅 (0) | 2023.07.04 |