인생은 삽질!

엘라스틱서치 ( elasticsearch ) 메모리 관련 (공식 홈피 번역) 본문

elasticsearch

엘라스틱서치 ( elasticsearch ) 메모리 관련 (공식 홈피 번역)

첫째토끼 2016.05.26 17:55

Heap: Sizing and Swapping

기본으로 1GB 로 설정 되는데 이는 일반적으로 매우 작은 값이고, 이대로 사용할 경우 문제가 있을 수 있다.

힙사이즈 변경하는 방법은 크게 2가지가 있다.

- 환경 변수에 ES_HEAP_SIZE 를 설정 : 서버라 실행 될 때 해당 환경 변수를 읽는다.

export ES_HEAP_SIZE=10g

- 커맨드 라인에 인수로 줄 수 있다.

./bin/elasticsearch -Xmx10g -Xms10g

* xms 와 xmx 는 동일하게 설정해 주느게 좋다 : 실행중에 사이즈를 변경하는 작업은 부담이다. ( 이는 jvm 공통)

Give(less than) Half Your Memory to Lucene

너무 큰 메모리 사이즈는 문제가 있다.

메모리 구조에 빠른 연산을 제공해 주기 위해 힙은 매우 중요하지만 루씬에서도 힙을 사용하기 때문에 이에 대해서도 고려해야 한다.

루씬의 성능은 OS 와 상호연관이 있는데 모든 메모리를 엘라스틱서치에 할당하게 되면 심각한 영향을 받을 수 있다.

일반적으로 50% 정도만 엘라스틱서치에 할당하고 나머지는 비워 두는게 좋다.

string fields 등에 analyze를 사용하지 않는다면 더 적은 메모리를 할당하는것도 고려해 봐야 한다. 좀 더 작은 힙 사이즈 할당은 faster GC와 루씬 메모리 캐시등에서 좀 더 나은 성능을 기대 할 수 있다.

Don't Cross 32GB!

HotSpot JVM 이 32GB 보다 적은 힙을 사용했을 때 object pointer 를 압축하는 트릭을 사용하기 때문이다.

자바에서 모든 오브젝트는 힙에 저장되며 포인터에 의해서 참조가 된다. OOP 가 이 오브젝트들을 가르키고 있으며 CPU의 영향을 받는다. (32bit or 64bit ) 포인터는 값이 저장된 위치의 byte 를 참조한다.

32Bit 시스템에서 최대 4GB 힙사이즈를 사용 할 수 있고 64bit 에서는 그 이상 쓸 수 있지만 64bit 이상의 포인터는 공간의 낭비다. 더 큰 포인터는 메인 메모리와 다양한 캐시간의 데이터 전송시에도 낭비다.

이 문제를 위해 자바에서는 compressed oops 라는 트릭을 사용한다. 이 트릭은 정확한 byte 를 가르키는 것이 아니라 object offsets 을 가르킨다. 이 것은 32bit 포인터가 4백만 byte를 가르키는 것이 아니라 4백만 object 를 가르킬 수 있다는 의미로 힙메모리가 32GB 까지 커질 수 있다느 의미다. 

32GB 를 넘게 되면 포인터는 다시 Ordinary Object Pointer 로 돌아가며 각 포인터의 사이즈는 늘어나게 된다. 즉, 32GB의 compressed oops 를 사용함으로써 40~50GB 의  힙 메모리 사용과 동일한 효과를 낼 수 있다.

그래서  메모리가 남는다 하더라도 32GB 를 넘지 말아라. 이는 메모리 낭비이고, CPU 성능을 떨어트리며, GC 에도 부담을 준다.

Just how far under 32GB should I set the JVM?

그때그때 달라요~ 

안정적으로 사용하고 싶다면 31GB, 를 사용하면 되는데 확인하기 위해서 HotSpot JVM 에 -XX:PrintFlagsFinal 옵션을 주면 UseCompressedOops 값으로 사용여부를 확인 할 수 있다. (java7과 java8에서 동일한 설정을 해도 compressedoops 를 사용여부가 달라진다. )

ES V2.2.0 부터 시작시 compressed oop 를 사용하는지 안하는지 로그로 보여준다. (언제나 그렇듯 로그는 꼼꼼히 봐야 하나보다. )

1TB 짜리 메모리가 있어요 어떻게 할까요?!

0. 그런거 쓰지마라.

- full-text search 를 자주 사용한다면 32GB 밑으로 설정을 하고 나머지는 루씬을 위해서 양보합시다. 나머지 여분을 캐시하게 되고 엄청 빠른 full-text search 를 할 수 있게 될것이다.

- 정렬/집합연산(숫자,날짜,위치,not_analyzed stirng에 대한)을 많이 한다면 역시 32GB 밑으로 설정하고 나머지는 OS 가 캐시하도록 두라.

- analyzed string 에 대한 정렬/집합연산을 많이 한다면? 안타깝게도 이는 fielddata 가 필요하다는 얘기고, 이는 힙을 필요로 한다. 단일 노드로 큰 메모리 할당을 하지 말고 하나 이상의 노드를 한 머신에 둬라.(물론 위의 32GB/50%메모리 여유분은 두고) 128GB 머신이라면 32GB짜리 노드 2개를 구동시키고 반절은 루씬을 위해 남기라.

위 구성을 위해서는 cluster.routing.allocation.same_shard.host : true 옵션을 줘야 한다. ( 이 옵션은 primary 와 replica 가 같은 머신에 놓여지는 것을 막아준다.)

Swapping Is the Death of Performance

메모리 스와핑은 성능에 최악이다. 방지하기 위해 ( 이것도 참조 : http://wishgone.tistory.com/78

sudo swapoff -a ( 이는 임시적인 방법이다. )

영구적으로 설정하기 위해서는 /etc/fstab 을 편집해야 하는데 이는 사용하는 OS문서를 참조.

위 설정( 머신 옵션을 끄는거 ) 할 수 없다면 swappiness 를 설정할 수도 있다. 이 값은 얼마나 적극적으로 메모리 스왑을 사용할지에 대한 옵션인데 일반적인 상황에서는 메모리 스왑 발생을 막을 순 있겠으나 긴급한 상황에서는 어쩔 수 없다. 

sysctl vm.swappiness=1 설정해랑 ( 0 보다는 1이 좋다. 몇몇 커널에서는 0으로 설정하면 OOM-killer 를 부른다 => 스왑을 일으키려는 프로세스를 죽여버리는듯.)

마지막으로 If neither approach is possible ( ? 2개다 가능하지 않는다면?) mlockfall 설정해라. JVM 이 메모리를 고정하여 사용할 수 있게 하고 OS 에 의한 스와핑을 막아 준다. 

elasticsearch.yml 파일에 bootstrap.mlockall : true 로 설정해라.


출처 : https://www.elastic.co/guide/en/elasticsearch/guide/current/heap-sizing.html


2 Comments
댓글쓰기 폼