ELK Stack

[Elasticsearch] 엘라스틱서치 메모리(2) - 왜 전체 시스템 메모리의 50%를 남겨두어야 하는가?

잭피 2022. 12. 26. 17:06

안녕하세요~ 잭피입니다!

오늘은 Elasticsearch를 실행시킬 때, 전체 메모리의 50%를 남겨두어야 하는 이유를 알아보겠습니다


엘라스틱서치 공식문서

먼저 엘라스틱서치 공식문서를 살펴볼까요?

공식문서에선 엘라스틱서치를 실행시킬 때, 보통 전체 메모리의 50%보다 낮게 설정하라고 가이드하고 있습니다

https://www.elastic.co/guide/en/elasticsearch/reference/7.17/advanced-configuration.html

 

Advanced configuration | Elasticsearch Guide [7.17] | Elastic

When running in a container, such as Docker, total memory is defined as the amount of memory visible to the container, not the total system memory on the host.

www.elastic.co


한글로 번역하면 다음과 같습니다.

엘라스틱서치는 JVM 힙 이외에도 메모리가 필요합니다
효율적인 네트워크 통신을 위해 off-heap buffers를 사용하고
파일에 대한 효율적인 액세스를 위해 운영 체제의 파일 시스템 캐시에 의존합니다
JVM 자체에도 약간의 메모리가 필요합니다
Elasticsearch가 설정으로 구성된 제한보다 더 많은 메모리를 사용하는 것은 정상입니다

 

엘라스틱서치 힙 메모리의 필요성


엘라스틱서치는 힙 메모리가 왜 필요로 할까요?
가장 중요한 이유는 다음과 같습니다
루씬은 디스크 상에 데이터를 찾을 수 있는 위치를 찾아내기 위해서 일부 데이터를 힙 메모리에 저장합니다

어느정도의 양을 힙 메모리에 저장할까?
보통 인덱스 GB(기가바이트)당 작은 MB(메가바이트)만큼을 힙 메모리에 저장합니다

작은 양처럼 보일 수 있지만,
로그를 저장하는 용도의 노드에는 TB(테라바이트) 상당의 데이터를 디스크에 저장합니다
그렇다면 GB(기가바이트) 상당의 힙 메모리가 필요로 하게 됩니다.

하지만, 엘라스틱서치에서는 30GB 이상의 힙메모리를 올리지 말기를 권장하고 있습니다

엘라스틱서치 메모리를 32GB보다 낮게 설정해야하는 이유에 대해 포스팅하였습니다.
궁금하시면 아래 내용을 참고해주세요~


2022.12.23 - [ELK Stack] - [Elasticsearch] 엘라스틱서치 메모리(1) - 왜 32GB보다 낮게 설정해야하는가? (Compressed OOP)

 

[Elasticsearch] 엘라스틱서치 메모리는 왜 32GB보다 낮게 설정해야하는가? (Ordinary object pointers / Compres

안녕하세요~ 잭피입니다! 오늘은 Elasticsearch jvm memory 및 Compressed OOP와 관련된 내용을 포스팅하려고 합니다! Ordinary object pointers? 엘라스틱서치 메모리를 설정하여 실행할 때, 32GB를 기준으로 한 가

jackjeong.tistory.com

실제 엘라스틱서치 7.초기버전의 경우 10TB 데이터 저장 시, 17기가의 힙 메모리가 필요했습니다
하지만 7.7버전 이후 2.5기가만 필요하도록 대폭 개선되었습니다.


엘라스틱서치에서 TB(테라바이트) 상당의 인덱스를 생성하는 건, 대부분 메트릭 및 로그를 인덱스하는 용도입니다
이 때는 대부분 메모리에서 hot bits를 유지하기 위해서 파일 시스템을 사용하는 기술들을 사용하지 않습니다
이렇게 사용되지 않고 있는 자원을 활용해서 힙의 사용률을 개선하였다고 합니다

엘라스틱서치는 힙 메모리 이외에

엘라스틱서치 내부에서 사용하는 루씬을 통해 시스템 메모리도 사용합니다


먼저, 루씬을 살펴볼까요?

루씬

루씬에 대해서 자세히 알고싶으시면 이전에 포스팅한 글을 읽어보세요
2020.11.08 - [Lucene/Lucene In Action] - [루씬 인 액션] 1. 루씬(Lucene)의 개념 및 구조

 

[루씬 인 액션] 1. 루씬(Lucene)의 개념 및 구조

루씬이란? → 루씬은 자바로 만들어진 고성능 정보 검색(IR : information retrieval) 라이브러리입니다 (IR : 문서를 검색하거나, 문서의 내용을 검색하거나, 문서와 연관된 메타 정보를 검색하는 과정

jackjeong.tistory.com

간단히 설명하고 지나가도록 하겠습니다.

루씬은 자바로 만들어진 고성능 정보 검색 라이브러리입니다
검색, 색인, 형태소 분석 등 검색 기본 기능을 제공합니다

인덱스(Index), 문서(Document), 필드(Field), 용어(Term) 개념을 가지고 있으며,
용어 < 필드 < 문서 < 인덱스 와 같은 각 단위의 집합 구조를 가집니다

이런 루씬 기반의 대표적인 검색엔진이 엘라스틱서치입니다

루씬의 색인 작업

루씬은 효율적인 색인 작업을 위해 내부적으로 일정 크기의 버퍼를 가집니다
이런 내부 버퍼를 In-memory-buffer(인메모리 버퍼)라고 합니다

색인 과정을 순서대로 봐볼까요?

1. 루씬 색인 작업 요청
2. 순서대로 인메모리 버퍼에 쌓임
3. 정책에 따라 처리 (용량 or 시간) - [refresh_interval 설정을 통해 튜닝 가능]
(내부 버퍼에 일정 크기 이상의 데이터가 쌓이거나 일정 시간이 지나는 경우 쌓인 데이터를 한꺼번에 모아서 처리)
4. 버퍼에 모여 한번에 처리된 데이터는 세그먼트 형태로 생성되어 즉시 디스크로 동기화
(이 시점부터 검색이 가능해집니다)

4번의 과정은 운영체제 입장에서 CPU 사용이 높은 작업입니다
루씬은 무거운 fsync 방식 대신 write 방식을 이용해 쓰기 과정을 수행합니다
이후 일정한 주기에 따라 물리적인 디스크 동기화 작업을 수행합니다

write()

파일을 저장할 때 사용하는 함수입니다
운영체제 내부 커널에는 시스템 캐시가 존재하는데 write() 함수를 이용하면 시스템 캐시에만 기록됩니다
실제 데이터는 특정 주기에 따라 물리 디스크로 쓰기 작업을 진행합니다

fsync()

저수준의 파일 입출력 함수입니다
내부 시스템 캐시의 데이터와 물리 디스크를 동기화하기 위한 목적으로 사용됩니다
실제 디스크 쓰기 작업을 진행하므로 많은 리소스를 사용합니다

Flush

위의 인메모리 버퍼 기반 처리과정을 Flush라고 합니다
Flush 처리에 의해 세그먼트가 생성되면 커널 시스템 캐시에 세그먼트가 캐싱되어 읽기가 가능해집니다
(write() 함수로 동기화가 수행됐기 때문에 커널 시스템 캐시에만 데이터가 생성된 상태)

Commit

이후 fsync() 함수를 호출하는 Commit을 진행하여 커널 시스템 캐시의 내용을 물리적인 디스크로 쓰는 작업을 진행합니다
시간이 지나면서 세그먼트들의 개수가 늘어나고, 부하도 증가하게 됩니다

Merge

늘어난 세그먼트들을 합치는 작업이 필요하며, 이를 Merge라고 합니다
Merge를 통해 세그먼트 수를 줄이게 되고, 성능도 좋아집니다
왜냐하면 검색 요청이 오면 루씬은 내부에 존재하는 모든 세그먼트를 검색하는데, 그 수가 줄어들면 검색 횟수도 줄어듭니다

엘라스틱서치 샤드는 루씬 인덱스의 확장입니다

루씬의 Flush = 엘라스틱서치의 Refresh
루씬의 Commit = 엘라스틱서치의 Flush
루씬의 Merge = 엘라스틱서치의 Optimize API


여기서 메모리와 관련된 작업은 루씬의 Flush, 엘라스틱서치의 Refresh 입니다
위 루씬 Flush의 기능은 다음과 같습니다

Flush 처리에 의해 세그먼트가 생성되면 커널 시스템 캐시에 세그먼트가 캐싱되어 읽기가 가능해집니다
(write() 함수로 동기화가 수행됐기 때문에 커널 시스템 캐시에만 데이터가 생성된 상태)

 

 

루씬의 커널 시스템 캐시 활용 (OS 레벨의 메모리 사용)

루씬의 시스템 캐시

루씬을 설명하다보니 이야기가 길어졌습니다.
다시 본론으로 들어가보겠습니다.

이렇게 루씬은 세그먼트 생성 및 관리를 위해 커널 시스템 캐시를 최대한 활용합니다
시스템 캐시운영체제가 가지고 있는 메모리 공간으로 커널 내부에 존재합니다

가장 중요한 페이지 캐시(운영체제 레벨의 캐시)를 살펴보겠습니다

페이지 캐시 (파일 시스템 캐시)

페이지 캐시의 기본 개념은 디스크에서 데이터를 읽은 후 사용 가능한 메모리에 데이터를 넣는 것으로,
다음 읽기는 디스크에서 읽지 않고 메모리에서 읽습니다

순서를 보면,
1. 애플리케이션이 디스크에서 데이터를 읽기 위해 시스템 호출을 실행
2. 커널/운영체제가 디스크로 이동하여 데이터를 페이지 캐시, 메모리에 넣습니다
3. 두 번째 읽기부터 커널에 의해 운영체제 메모리 내의 페이지 캐시로 리다이렉션됩니다

디스크에 있는 데이터를 엑세스하는 것보다, 페이지 캐시는 훨씬 더 빠르게 데이터를 액세스할 수 있습니다

엘라스틱서치 메모리에 대한 권장사항이 총 메모리의 50% 이하인 이유 중 하나입니다


나머지 절반은 페이지 캐시로 사용할 수 있습니다
또한, 메모리가 낭비되지 않고 페이지 캐시에 재사용됩니다

요약하면,
페이지 캐시는 운영체제의 기본 메모리에 전체 인덱스 데이터 구조를 로드하여 임의 검색을 더 빨리 실행할 수 있도록 지원합니다

이 이외에도 샤드 레벨 요청 캐시, 쿼리 캐시 등 다양한 캐시를 사용합니다.
이 캐시들은 자바 힙 메모리를 사용하는 캐시로 설명은 생략하겠습니다.

캐시에 관련된 내용은 엘라스틱 공식 블로그를 참고하여 적었습니다.
https://www.elastic.co/kr/blog/elasticsearch-caching-deep-dive-boosting-query-speed-one-cache-at-a-time

 

Elasticsearch 캐싱 심층 분석: 한 번에 하나의 캐시로 쿼리 속도 향상

Elasticsearch에서 다양한 캐시를 활용하여 데이터를 최대한 빨리 검색하는 방법에 대해 알아보세요. 페이지, 샤드 및 쿼리 캐싱에 대해 자세히 살펴보고 각 캐싱을 사용하여 쿼리 속도를 높이는 방

www.elastic.co

 

정리

엘라스틱서치의 메모리에 관련된 내용을 찾아 정리하였습니다.
그러다보니 루씬의 색인작업까지 주절주절 정리한 느낌이네요.

결국 간단히 정리하면 아래와 같습니다

1. 엘라스틱서치는 내부적으로 루씬을 사용한다
2. 루씬은 커널 시스템 캐시를 사용한다. 그 중 파일 시스템 캐시 활용이 많은 부분을 차지한다
3. 파일 시스템 캐시를 위해 OS 가용 메모리 중 50%를 남겨둔다

엘라스틱서치 실행 시,
파일 시스템 캐시 활용을 위해 OS 가용 메모리 중 50%를 남겨두자