궁금한게 많은 개발자 노트

[ ES ] Elasticsearch Heap memory 크기 설정 본문

DevOps

[ ES ] Elasticsearch Heap memory 크기 설정

궁금한게 많은 개발자 2023. 8. 4. 19:45

Elasticsearch(ES)는 JAVA기반으로 실행되기 때문에 Heap 메모리를 얼마나 설정하느냐에 따라 성능에 큰 차이를 가지게 됩니다. 이에 따라 ES에서는 적절한 Heap size를 어떻게 판단하여 설정하는지에 대해 글을 작성해보고자 합니다.

너무 큰 Heap size는 GC(Garbage Collection)이 발생할 경우 성능에 부하를 줄 수 있고, 너무 작은 경우는 Out-of-Memory가 발생할 수 있습니다. ES에서 가이드하는 적절한 Heap Size는 어느 정도인지 알아보고자 합니다.

 

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

 

ES는 JAVA기반으로 JVM위에서 동작하기에, JVM의 특성과 Heap Memory Size의 설정은 상관관계가 있습니다.

JVM은 자신에게 필요한 데이터들을 메모리 공간에 올려놓기 위해 Heap 메모리를 필요로 합니다. 이 곳에 연산을 위해 필요한 다양한 데이터들을 올려놓고 사용하게 됩니다. 

이 때 데이터들을 Object라 부르며 이 Object에 접근하기 위한 메모리 상의 주소를 OOP(Ordinary Object Pointer)라는 구조체에 저장합니다. OOP는 시스템의 아키텍처에 따라 32bit 혹은 64bit 기반의 크기를 가지게 되는데, 32bit라면 2^32인 4GB까지의 주소 공간을 가리킬 수 있게 됩니다. 반면 64bit의 경우에는 이론상 2^64인 18EB까지의 주소 공간을 가리킬 수 있습니다. 하지만, 64bit의 경우 32bit보다 포인터 자체의 크기가 크기 때문에 비교적 성능이 낮을 수 밖에 없습니다.

(더 많은 연산을 필요로 하고, 더 많은 메모리 공간을 차지)

 

그래서 JVM은 64bit 시스템 아키텍처를 갖고 있더라도 Heap 영역이 4GB보다 작다면 32bit기반의 OOP를 사용합니다. 문제는 Heap 사이즈가 4GB를 넘을 때 발생합니다. 32bit 기반의 OOP로는 4GB 이상의 주소를 가리킬 수 없기 때문입니다.

그렇다고 64bit 기반의 OOP를 항상 사용하기에는 갑작스런 성능 저하가 발생합니다. (32bit 시스템에서는 사용불가)

 

 

 

 

[ Compressed OOP ]

 

그래서 나온 것이 Compressed OOP입니다. Native OOP는 OOP가 바로 Object의 주소를 가리킵니다. 하지만 Compressed OOP의 경우에는 OOP가 주소가 아닌 주소 offset을 가리키고 그 오프셋은 기존 주소의 8의 n배수로 계산되여, 기존 1번은 8번 주소를, 2번은 16번 주소를 가리키게 됩니다. 이렇게 되면 8배 많은 주소 공간을 표시할 수 있습니다.

8의 배수로 Object를 저장하기 때문에 실제 메모리 주소를 참조하기 위해서는 left shitt 연산이 필요합니다.

native oop = (compressed oop << 3)

shift연산은 CPU연산 중에서 빠른 편에 속하기에 주소 변환에 사용되는 CPU부하가 크지 않아 성능 저하에 큰 영향을 미치지 않습니다. 다만 일부 메모리 영역이 버려질 수 있지만, 그만큼 더 큰 Object를 채워 넣을 수 있기에 실제 손실은 그리 크지 않다고 합니다.

 

 

위에서 설명한 Compressed OOP를 기반으로, 아래  ES에서 가이드하는 적당한 Heap Size는 32GB가 넘지 않는 이유에 대해서 어느정도 이해가 가는 것 같습니다.

(사용 가능한 전체 메모리의 50%를 넘지 않게 최대 최소 힙 크기를 설정하는 것을 권장합니다)

The heap size should be based on the available RAM:
Set Xms and Xmx to no more than 50% of your total memory. Elasticsearch requires memory for purposes other than the JVM heap. For example, Elasticsearch uses off-heap buffers for efficient network communication and relies on the operating system’s filesystem cache for efficient access to files. The JVM itself also requires some memory. It’s normal for Elasticsearch to use more memory than the limit configured with the Xmx setting.

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.

Set Xms and Xmx to no more than the threshold for compressed ordinary object pointers (oops).The exact threshold varies but 26GB is safe on most systems and can be as large as 30GB on some systems. To verify you are under the threshold, check the Elasticsearch log for an entry like this:

전체 물리 메모리가 64GB가 넘는 다면 그 절반 수준에서 32GB를 넘지 않는 30-31GB로 Heap 크기를 설정하는 것이 가장 효율적입니다. 32GB가 넘어가면 64bit OOP를 사용하게 되어 급격히 성능이 저하됩니다.

반대로 전체 물리 메모리가 32GB라면 16GB수준에서 Heap 크기를 설정하면 됩니다.

 

전체 물리 메모리의 절반정도로 Heap크기를 설정하는 이유는, ES는 힙 메모리 외에도 빠른 검색과 인덱싱 성능을 위해 파일 시스템 캐시를 적극적으로 활용합니다. 이 또한 상당한 메모리를 사용하므로 충분한 여유 메모리를 확보하지 않으면 성능상 문제가 생길 수 있습니다.

 

 

 

 

[ Zero Based Heap Memory ]

 

또 하나의 이슈는 Zero Based Heap Memory입니다. JVM은 실행될 때 OS로 부터 Heap영역을 할당받게 되는데 이때 할당받은 주소가 0부터 시작한다면 주소 공간을 가리킬 때 left shift연산만 사용하면 됩니다.

하지만 할당 받은 주소 공간이 0부터 시작하지 않는다면 shift연산 외에 heap의 시작 주소 만큼 더하기 연산이 필요하게 됩니다. 그렇게 된다면 주소 변환에 시간이 더 걸리게 되므로 CPU에 조금의 부하가 더해지고 성능 저하로 이어집니다.

 

Zero Base로 할당받을 수 있는 Heap Memory의 임계치가 어디까지 인지 JAVA_OPTS에 옵션을 추가하여 확인할 수 있습니다.

-XX:+UnlockDiagnosticVMOptions -XX:+PrintCompressedOopsMode

정리하자면, 적당한 Heap메모리는 전체 메모리의 절반 정도 수준에서 전체 메모리가 크다면 32GB보다 작은 값으로 설정.

Compressed OOP를 사용할 수 있고, Zero Base인 임계치를 확인하여 설정합니다.

  • 시스템 전체 메모리의 절반
  • Compressed OOP를 사용할 수 있는 32GB이하
  • Zero Based Compressed OOP를 사용할 수 있는 임계치 확인하여 설정

 

 

 

 

[ES Heap size 설정하는 법]

 

Elasticsearch는 기본적으로 1GB의 Heap 사이즈로 구성이 되어 있습니다. 이 기본값은 대부분의 클러스터 구성에서 매우 작기 때문에 Elasticsearch에서 충분히 사용이 가능하도록 메모리 크기를 늘려 주어야 합니다.

Elasticsearch에서 힙 크기를 변경하는 방법에는 두 가지가 있습니다. 가장 쉬운 것은 ES_HEAP_SIZE 이라는 환경 변수를 설정하는 것 입니다. 서버 프로세스가 시작되면이 환경 변수를 읽고 이에 따라 힙을 설정합니다. 예를 들어, 다음과 같이 명령 행을 통해 설정할 수 있습니다.

export ES_HEAP_SIZE=16g

또는 프로세스를 시작할 때 JVM 플래그를 통해 힙 크기를 전달할 수 있습니다. 쿠버네티스에서 Statefulset을 통해 배포할 때도 해당 변수 값을 env에서 사용하여 설정할 수 있습니다.

ES_JAVA_OPTS = "-Xms10g -Xmx10g" ./ bin / elasticsearch

Heap Size의 최소, 최대 값을 정하는데 같은 값으로 설정하는 것이 좋습니다. 다르게 하면 heap size를 변경하는 도중에 node가 중지될 수 있기 때문에 같은 값으로 설정해주는 것을 추천합니다.

 

 

 

 

 

Comments