Executor는 Worker 노드에서 실행되는 JVM 프로세스 역할을 한다. 따라서 JVM 메모리 관리를 이해하는 것이 중요하다.
JVM 메모리 관리는 두가지로 나눌 수 있다.
Object는 JVM heap에 할당되고 GC에 바인딩된다.
Object는 직렬화에 의해 JVM 외부의 메모리에 할당되고 Application에 의해
관리되며 GC에 바인딩되지 않는다.
일반적으로 Object의 읽기 및 쓰기 속도는 On-Heap > Off-Heap > DISK 순서로 빠르다.
Spark의 기존 메모리 관리는 정적인 메모리 분할(Memory fraction)을 통해 구조화 되어 있다.
메모리 공간은 3개의 영역으로 분리되어 있으며, 각 영역의 크기는 JVM Heap 크기를 Spark Configuration에 설정된 고정 비율로 나누어 정해진다.
spark.executor.memory: executor가 사용할 수 있는 총 메모리 크기를 정의한다.
각 영역 메모리에 상주된 데이터들은 자신이 위치한 메모리 영역이 가득 찬다면
Disk로 Spill 된다.
‘Storage 영역의 경우 Cache 된 데이터는 전부 Drop되게 된다. 모든 경우에서 데이터 Drop이 발생하면 I/O 증가 혹은
Recomputation으로 인한 성능 저하가 나타나게 된다.’
먼저, Executor에 관한 몇 가지 기본 전제를 확인해보자.
executor는 캐싱과 실행을 위한 JVM을 가지고 있다.
executor와 driver 사이즈는 하나의 노드나 컨테이너에 할당된 자원보다 많은 메모리나 코어를 가질 수 없다.
그럼 다수의 작은 사이즈의 executor로 구성하는게 좋을까, 소수의 큰 사이즈의 executor로 구성하는게 좋을까?
하나의 파티션을 처리할 자원이 충분하지 않을 수 있으며, OOM 또는 disk spill 이 생길 수 있다.
따라서 자원이 허용된다면, executor는 최소 4GB 이상으로 설정하는 것을 권장한다.
너무 큰 executor는 힙 사이즈가 클수록 GC가 시작되는 시점을
지연시켜 Full GC로 인한 지연이 더욱 길어질 수 있다.
executor 당 많은 수의 코어를 쓰면 동시에 스레드가 많아지면서
스레드를 다루는 HDFS의 제한으로 인해 성능이 더 떨어질 수도 있다.
따라서 executor당 코어를 최대 5개로 하는 것을 권장하며, 이를 넘게 되면
성능 향상에 도움되지 않으며 CPU 자원을 불필요하게 소모하게 된다.
Reference
https://jaemunbro.medium.com/spark-executor-%EA%B0%9C%EC%88%98-%EC%A0%95%ED%95%98%EA%B8%B0-b9f0e0cc1fd8
https://jaemunbro.medium.com/apache-spark-partition-%EA%B0%9C%EC%88%98%EC%99%80-%ED%81%AC%EA%B8%B0-%EC%A0%95%ED%95%98%EA%B8%B0-3a790bd4675d
https://spidyweb.tistory.com/328