배치 프로세싱(Batch processing) - 2
조인
여러 데이터셋에서 한 레코드가 다른 레코드와 연관되어 있는 것은 일반적입니다. 관계형 모델에서는 외래키, 문서 모델에서는 문서 참조, 그래프 모델에서는 간선이라고 부릅니다. 비정규화를 통해서 이러한 조인을 줄일 수는 있지만 완전한 제거는 어렵습니다. 배치 처리에서의 조인은 데이터셋 내의 모든 연관 관계를 다루는 것을 의미합니다. 데이터베이스에서 적은 수의 레코드만 조인 질의한다면 색인을 사용해서 레코드의 위치를 빨리 찾을 수 있지만 맵리듀스에서는 이러한 색인 개념이 없습니다. 그래서 모든 데이터를 읽어야 하는 문제가 있지만 일반적으로 배치 처리에서는 대량의 레코드를 대상으로 집계 연산을 주로 수행하기 때문에 전체 데이터를 읽는 것을 합리적인 연산입니다.
Sort-merge 조인
맵리듀스 프레임워크에서 키로 매퍼의 출력을 파티셔닝하여 키-값 쌍으로 정렬한다면 아래의 그림과 같이 동일한 사용자 아이디내에 사용자 활동 이벤트와 사용자 레코드가 리듀서의 입력으로 들어가게 됩니다.
리듀서는 특정 사용자 ID의 모든 레코드를 한 번에 처리하므로 한 번에 한 명의 레코드만 메모리에 유지하면 되고 네트워크로 다른 요청을 할 필요가 없습니다. 매퍼의 출력이 키로 정렬된 후에 리듀서가 조인의 양측의 정렬된 레코드를 병합하기 때문에 sort-merge 조인이라고 합니다.
쏠림(Skew) 다루기
조인 연산에서 키 하나에 너무 많은 데이터가 존재하는 경우에 문제가 발생할 수 있습니다. 주로 맵리듀스에서 데이터가 쏠리는 현상을 핫스팟이라고 합니다. 하나의 리듀서가 다른 리듀서들 보다 훨씬 많은 데이터를 처리해야 하기 때문에 가장 느린 리듀서가 작업을 완료할 때까지 후속 작업들은 대기를 해야 하는 문제가 있습니다. 이를 해결하는 방법은 여러 가지 방법이 있습니다. 맵사이드 조인을 사용하여 해결하거나 핫 키로 레코드를 그룹화하고 집계할 때 두 단계로 처리하는 방법입니다. 두 단계로 처리하는 방법은 다음과 같습니다. 첫 번재 맵리듀스에서는 맵 단계에서 레코드를 임의의 리듀서로 보내서 처리하여 핫 키 레코드의 일부를 그룹화 하고 간소화된 값을 출력합니다. 두 번째 단계에서는 첫 번째 단계의 출력으로 나온 값을 키별로 모두 결합해 하나의 값으로 만듭니다.
맵-사이드 조인
일반적으로 맵리듀스에서 조인 로직은 리듀서가 처리하기 때문에 리듀스-사이드 조인이라고 합니다. 일반적인 리듀스-사이드 조인의 경우 입력 데이터의 특별한 경우없이 잘 동작합니다. 여기서 특별한 경우란 맵-사이드 조인이 동작할 수 있는 경우를 이야기 하는데 이것은 뒤에서 설명드리겠습니다. 리듀스-사이드 조인에서 입력을 병합하는 과정이 비용이 많이듭니다. 맵리듀스 단계에서 허용된 메모리 버퍼 크기에 따라 디스크에 여러번 쓸 수 있기 때문입니다. 디스크에 써야하는 것은 결국 처리 속도를 더디게 합니다. 그러나 입력 데이터가 특별한 경우를 만족하는 경우에는 맵-사이드 조인을 사용할 수 있습니다. 이 방식을 사용하면 더욱 빠르게 조인을 수행할 수 있습니다. 이러한 맵-사이드 조인의 종류를 살펴보도록 하곘습니다.
브로드캐스트 해시 조인
브로드캐스트 해시 조인이란 메모리에 올릴 정도로 데이터의 양이 충분히 작은 데이터 셋과 매우 큰 데이터 셋을 조인할 때 사용하는 방법입니다. 브로드캐스트
라는 것은 큰 입력 파티션 하나를 처리하는 매퍼가 작은 입력 전체를 미리 읽는 다라는 것이며 해시
는 작은 입력을 해시 테이블을 구성해둔다라는 의미입니다. 그러면 두 입력을 빠르게 조인할 수 있습니다.
파티션 해시 조인
파티션 해시 조인은 두 입력 모두를 같은 키와 같은 해시 함수 기반으로 동일한 개수로 파티셔닝하여 조인하는 방식입니다. 이 방식은 각 매퍼의 해시 테이블에 적재해야 할 데이터의 양을 줄여주는 장점이 있습니다. 이러한 방식을 하이브에서는 버킷 맵 조인이라고 합니다.
맵-사이드 머지(merge) 조인
이 방식은 입력 데이터셋이 동일한 방식으로 파티셔닝되어 있고 같은 키를 기준으로 정렬되어 있는 경우 사용할 수 있는 맵-사이드 조인입니다. 일반적으로 리듀서에서 동작하는 sort-merge 조인과 동일한 방식으로 동작하기 때문에 입력 크기가 메모리에 적재 가능한지는 고려하지 않아도 됩니다. 맵-사이드 머지 조인이 가능하다는 이야기는 이미 입력 데이터셋이 정렬되어 있다는 의미입니다.
배치 처리 출력에 관한 철학
앞서 설명한대로 유닉스 철학은 데이터플로우가 프로그램이 입력을 읽어 출력을 내놓는다
였습니다. 이 과정에서 입력은 변하지 않고 새로운 출력이 이전 출력을 완벽하게 교체했습니다. 이 과정에서는 사이드 이펙트가 없습니다. 마찬가지로 맵리듀스도 동일한 철학으로 출력을 취급합니다. 입력은 불변으로 처리하고 외부 데이터베이스에 기록하는 등의 사이드 이펙트를 피해 좋은 성능을 내면서 유지보수가 간단합니다.
하둡과 분산 데이터베이스 비교
맵리듀스와 MPP(Massively parallel processing) 데이터베이스의 가장 큰 차이점은 MPP 데이터베이스에서는 분석 SQL 질의를 병렬로 수행하는 것에 초점을 둔 반면 맵리듀스와 분산 파일 시스템의 조합은 아무 프로그램이나 실행할 수 있는 운영체제와 비슷한 속성을 제공해줍니다.
데이터베이스의 경우 특정 모델(관계형이나 문서형 등)에 따라 데이터를 구조화해야 합니다. 반면에 분산 파일 시스템(HDFS)에서 파일은 어떤 데이터 모델과 인코딩을 사용해서도 기록할 수 연속된 바이트일 뿐이기 때문에 텍스트나 이미지 비디오 등 어떤 형태로 저장할 수 있습니다. 이와 같이 데이터베이스를 사용하면 데이터베이스 특성에 맞게 특화된 저장 형태로 데이터를 가져오기 전에 데이터와 질의 형태를 모델링해야 합니다. 물론 데이터베이스에는 사용자가 작업하기 좋은 양질의 데이터를 갖게 됩니다. 하지만 현실에서는 이상적인 데이터 모델을 만들려고 하기 보다는 데이터를 빨리 사용 가능하게 만드는 것이 더 가치가 있습니다. 데이터 호수(data lake)와 같이 원시 데이터를 수집하고 스키마 설계는 나중에 고민하면 데이터의 수집 속도가 올라가게 됩니다.
또한 MPP 데이터베이스의 경우 SQL 질의를 사용해서 다양한 데이터 처리가 가능하지만 모든 종류의 처리를 표현할 수 없습니다. 예를 들어 머신러닝이나 추천 시스템, 전문 검색 색인 구축 등과 같은 것을 처리하려면 더 범용적인 데이터 처리 모델이 필요합니다. 맵리듀스와 HDFS를 사용하면 이러한 범용적인 데이터 처리가 가능합니다. 이와 같이 하둡 위에서 다양한 처리 모델이 개발되었는데 대표적인 것이 SQL 질의 실행 엔진인 하이브, 랜덤 엑세스가 가능한 HBase, MPP 스타일의 분석 데이터베이스인 임팔라 등이 있습니다. 하둡에서 다양한 처리 모델은 모두 단일 공유 클러스터에서 실행되고 분산 파일 시스템 상에 존재하는 동일한 파일에 접근가능합니다. 이런 특성을 통해 데이터를 따로 옮길 필요 없이 데이터에 대한 새로운 처리 모델을 적용하는 것이 용이합니다.
데이터플로우 엔진
맵리듀스의 경우 입력을 생성하는 잡이 완료되어야지만 시작이 가능합니다. 반면 유닉스 파이프로 연결된 프로세스는 동시에 시작되고 출력이 생산되는 동시에 바로 소비가 됩니다. 이와 같이 장비들 사이에 부하가 달라 하나의 장비에 잡이 쏠리는 경우 쏠린 장비의 태스크가 완료되는 시간이 오래걸려 최종적으로 전체 수행 시간이 느려지는 문제도 발생합니다. 이러한 문제를 해결하기 위해 분산 배치 처리 연산을 수행하는 엔진들이 개발됐습니다. 대표적인 것이 스파크, 테즈, 플링크가 있습니다. 설계 방식에는 차이가 있지만 전체 워크플로우를 독립된 하위 작업으로 나누는 것이 아닌 작업 하나로 처리한다는 것입니다. 이러한 엔진들은 여러 처리 단계를 통해 데이터 플로우를 명시적으로 모델링하기 때문에 데이터 플로우 엔진이라고 부릅니다. 또한 맵리듀스와 달리 맵과 리듀스를 번걸아 수행하는 규칙을 엄격히 지킬필요가 없습니다. 대신에 더 유연한 함수들을 제공하고 그 함수들을 조합할 수 있습니다. 스파크의 트랜스포메이션과 액션이 대표적인 예입니다.
내결함성
맵리듀스는 중간 상태를 분산 파일 시스템에 모두 저장하기 때문에 쉽게 내결함성을 확보할 수 있습니다. 스파크와 플링크, 테즈는 HDFS에 중간 상태를 쓰지 안힉 때문에 내결함성 확보를 위해 다른 접근 방법을 사용합니다. 재계산이 가능하려면 프레임워크에서 주어진 데이터가 어떻게 연산되는지 추적해야 하는데 스파크에서는 데이터의 조상을 추적하기 위해 데이터를 RDD(Resilient Distributed Dataset)로 추상화합니다. 플링크는 연산자 상태를 체크포인트로 남겨 작업 실행 중 실패한 연산자를 다시 수행할 수 있습니다.
정리
이번 포스트에서는 맵리듀스에서 사용되는 조인 알고리즘에 관해 살펴보았습니다. 이러한 조인 알고리즘은 MPP 데이터베이스나 데이터플로우 엔진들에서도 동일하게 사용합니다. 그리고 맵리듀스와 MPP 데이터베이스와의 차이점에 관해 살펴보았습니다. 그리고 맵리듀스가 가진 한계를 새로운 데이터플로우 엔진들이 어떻게 해결했는지에 관해 살펴보았습니다.