- 루씬은 라이브러리 형태로 사용할 수 있도록 jar 형태로 배포됨
- 엘라스틱서치는 루씬 라이브러리를 임포트하는 방식으로 활용함.
- 엘라스틱 서치가 실행되어 인스턴스가 만들어지면 둘 다 하나의 JVM 위에서 동작하게 됨.
실행 시 자바 8 이상을 사용해야 하는 이유
- 배경
- 기존 32비트 운영체제 - 메모리 주소를 $2^{32}$ 밖에 가리킬 수 없기 때문에 4GB의 물리 메모리밖에 인식하지 못했기에 더 많은 메모리를 사용하기 위해 64비트 운영체제로 빠르게 전환됨.
- 다수의 CPU가 탑재된 환경에서는 멀티 스레드 방식과 비교했을 때 어려움이 존재함.
- Java는 8 버전에서 많은 변화가 생겼는데, Stream, Lambda를 이용했을 때 멀티코어로 함수를 동작시킬 수 있기 때문에 효과적이다.
- Java 8에서는 다양한 JVM 옵션을 제공함.
- 엘라스티서치는 많은 메모리를 사용하기 때문에 JVM 옵션 튜닝이 필수적
- 엘라스틱서치는 분산 시스템 특성상 Scale In/Out 이 빈번히 발생함.
- 장애 복구, ReIndex 작업에 의한 데이터 리밸런싱
- jvm.option 파일에는 JVM 힙 크기가 default로 1GB 설정되어 있는데, 운영 환경에서는 더 크게 설정 필요.
힙 크기를 32GB 이하로 유지해야 하는 이유
- 배경
- JVM은 처음 실행 시
Xms
에 설정된 힙 크기로 동작, 힙이 부족할 때 Xmx
에 설정된 힙 크기까지 늘어남.
- 늘어나는 과정에서 성능 저하가 일어날 수 있기 때문에 처음부터
Xms
, Xmx
크기 설정을 같게 하는 것이 유리함.
- 운영체제에 50%의 메모리 공간을 보장하자.
- 루씬은 커널 시스템 캐시를 최대한 활용
- 물리적인 메모리 공간의 50%는 운영체제에, 나머지 50%는 엘라스틱서치 힙에 할당
- Hot-spot JVM의 Object Pointer 정책 때문
- 모든 자바 기반 애플리케이션에 해당
- 모든 JVM은 기본적으로 32비트 Object Pointer 사용
- 힙 영역에 생성된 객체에 접근하기 위한 포인터 주소 OOP(Ordinary Object Pointer)들은 CPU의 처리 단위에 따라 동작 방식이 조금씩 다르다.
- 64비트 시스템에서는 포인터 1개를 64비트로 표현, 많은 메모리 공간의 낭비 발생
- 메모리의 물리적인 공간 활용성 하락
- 캐시 적중률을 높이기 위한 주메모리와 캐시 사이에서 지속적인 스위칭이 발생함.
- Compressed OOP
- 포인터를 압축해서 표현하는 일종의 트릭
- 포인터가 객체의 정확한 메모리 주소를 가리키는 것이 아닌 상대적인 Object Offset을 가리키도록 변형하여 동작시키는 것
- 일반적인 OOP는 $2^{32}$ 까지 메모리 공간을 가리킬 수 있고, Compressed OOP는 $2^{32} \times 8$ 까지 가능.
- JVM은 힙 크기가 32GB 넘어가는 순간 Compressed OOP를 일반적인 64비트 OOP로 자동 변환함.
- 주로 운영체제, 엘라스틱서치 힙에 할당하는 메모리 비율은 50%로 하되, 다음과 같은 경우에는 운영체제에 더 많이 할당한다.
- 전문(Full Text) 검색이 주목적
- 루씬이 시스템 캐시를 통해 메모리를 최대한 사용할 수 있도록
- 루씬은 내부적으로
mmap
을 통해 시스템 캐시를 이용해 세그먼트 캐싱이 가능하기 때문
- 정렬/집계 작업이 많은 경우
- Not Analyzed 필드에서 수행되는 경우는
DocValues
를 사용하기에 힙 공간 거의 사용하지 않음.
- DocValues는 메모리를 효율적으로 사용하기 위해 jvm heap 메모리가 아닌 운영체제 os의 파일 시스템 캐시를 사용해 색인 시 디스크를, 검색시 시스템 캐시를 이용하는 디스크 기반 데이터 구조이다
- Analyzed 필드에서 수행되는 경우는
DocValues
를 사용할 수 없고 fieldata
힙 캐시를 사용해야 함.
<aside>
💡 하나의 물리 서버에 다수의 엘라스틱서치 인스턴스 실행 시 주의점
→ 고가용성에 문제가 생길 수 있음. 이를 방지하기 위해 인스턴스 실행 시 primary 샤드와 replica 샤드가 같은 서버에 배치되는 것을 방지하게 도와주는 cluster.routing.allocation.same_shard.host
옵션 제공
</aside>
- JVM 힙 메모리가 0번지부터 시작되지 않는다.
- 실제 시작 번지 앞쪽의 일부 메모리 공간을 활용 불가
- Compressed OOP는 100% 시프트 연산만으로는 처리 X, 시프트 연산으로 생성된 값에 특정 번지수를 더하는 연산이 추가로 병행되어야 계산 가능.
- 이러한 성능을 개선하기 위해 Zero-Based Compressed OOP 개념 도입
- JVM 시작 시 힙 메모리 시작 번지가 0번지로 되도록 논리적으로 강제함.
- 따라서 시프트 연산만으로도 객체의 포인터 계산 가능
- 29.99GB 이하로 힙 크기를 설정해야만 동작