HDFS 기반 스토리지 엔진


하둡 에코 시스템에서 사용되는 원조 스토리지 엔진은 HDFS 다. HDFS는 순차 스캔으로 접근되며 삭제는 안 되고 추가만 가능한 대규모 데이터를 저장하는 데 아주 탁월한다. 하지만 아래의 경우 고민해볼 필요가 있다.

  • 임의의 레코드 조회나 수정 등의 접근
  • 문서 검색

대다수 작업에서 대규모의 다양한 데이터셋을 다루지만, 본질적으로 분석 작업은 아니다. 그래서 분석 용도에 사용할 수 있는 몇 가지 프로젝트가 새로 개발 됐으며, 이미 존재하던 프로젝트의 하둡 버전이 나오기도 했다.



Apache HBase


초기 인터넷 회사 중 일부는 수백억에서 수조 개의 레코드를 저장하고 효율적으로 조회하고 수정하는 방법을 필요로 했는데, 이런 수요가 Apache HBase의 탄생으로 이어졌다. HBase는 임의의 접근 가능한 키-값 구조의 반정형 데이터를 HDFS에 저장한다. 대부분의 하둡 프로젝트와 마찬가지로, HBase 프레임워크의 초기 설계도 구글의 논문인 빅테이블을 기초로 만들어졌다. HBase는 본질적으로 HDFS가 적합하지 않은 임의 접근 읽기/쓰기 작업을 HDFS가 적합한 순차 I/O 작업으로 변환하는 방법을 제공한다.

HBase 는 관계형 데이터 스토어가 아니며, 분산 테이블에 셀(cell)이라 불리는 키-값 쌍의 반정형 데이터 형식으로 데이터를 저장한다. HBase는 셀키를 위계 구조(hierarchy)를 가지도록 세분화해서, 관련된 셀은 함께 효율적으로 접근할 수 있다. 키의 첫 번째 부분은 로우 키(row key)라 하는데, 셀의 논리적인 그룹인 로우(row)를 정의하는 데 사용된다. 키의 나머지 부분은 컬럼 패밀리(column family)로 세분화되는데, 컬럼 패밀리 역시 셀의 논리적 그룹을 나타낸다. 컬럼 패밀리는 메모리와 디스크에 분리되어 저장되고, 테이블 하나에 컬럼 패밀리는 일반적으로 몇 개밖에 저장되지 않는다. 키 스키마 중에서 컬럼 패밀리는 테이블이 생성될 때 정의돼야 한다. 컬럼 패밀리는 컬럼 한정자(column qualifier)로 세분화되는데, 하나의 로우에 수백만 개의 컬럼 한정자가 있을 수 있다. 마지막으로 각 셀은 버전을 나타내는 타임스탬프를 가지고 있다. 동일한 키를 갖지만 각기 다른 타임스탬프를 가진 여러 셀은 서로 다른 버전으로 저장될 수 있다. HBase는 타임스탬프 외에 키의 각 구성요소와 값을 바이트 배열로 취급한다. 그래서 HBase는 셀의 어느 부분에 대해서도 타입 관련 정보를 모르며, 타입 관련 제약사항이 없으므로 결국 반정형 데이터 스토어라고 할 수 있다.

HBase에서는 셀이 키 구성요소에 따라 정렬되어 저장된다. 먼저 로우 키 기준으로 정렬된 후 컬럼 패밀리, 컬럼 한정자, 마지막으로 타임스탬프 기준을 정렬된다. HBase는 수평 파티셔닝을 사용한다. 그래서 테이블 안의 셀들은 파티션되어 클러스터에 분산 저장된다. 테이블의 로우 키를 위한 저장 공간도 리전(region)이라는 파티션으로 나뉜다. 각 리전은 정렬된 로우 키를 중복 없이 나눠서 보유한다. 리전 사이의 경계는 리전 스플릿(region split)이라고 부른다. 예를 들어 로우가 임의의 알파벳 접두어를 가진다면 26개의 리전을 가진 테이블을 만들 수 있다. a로 시작하는 키를 가진 로우는 첫 번째 리전에 저장되고, c로 시작하는 로우 키를 가진 로우는 세번째, z로 시작하는 로우 키를 가진 로우는 마지막 리턴에 저장된다. 리전은 나중에 수동으로 추가될 수 있으며, 로우 접근이 많은 리전은 HBase에 의해 자동으로 나뉠 수도 있다. 이를 통해 테이블은 쉽게 분산될 수 있으며 높은 확장성을 가지게 된다.

운영 관점에서 HBase의 학습 곡선은 누구에게나 꽤 가파를 수 있다. 테이블과 셀 키의 설계는 성능에 절대적인 영향을 미치므로 매우 중요하다. 테이블 레이아웃을 바르게 설계하려면 HBase의 작동 방식을 실무적으로 잘 이애해야 한다. 그렇지 않으면 결국 테이블 풀 스캔, 리전 핫스팟, 잦은 압축 발생 같은 좋지 않은 결과로 이어지게 된다. HBase는 상대적으로 작은 범위 스캔을 거친 작은 그룹으 ㅣ셀에 대해 적절하게 분산된 읽기/쓰기 요청을 처리하는 임의 접근 I/O처리에 뛰어나다. 그래서 분석 작업에서 처리하는 수준의 대규모의 데이터를 HBase로 스캔하면 실행 시간이 상당히 오래 걸릴 수 있으며, 이런 작업은 직접 HDFS 파일을 대상으로 수행하는 편이 낫다.



Apache KUDU


전통적인 하둡 기반 아키텍처에서 가장 힘든 부분 중 하나느, 분석 작업에서의 높은 처리량과 동일한 데이터를 읽을 때 빠른 임의 접근 읽기를 모두 지원하려면 여러가지 스토리지 엔진을 사용해야 한다는 점이다. 임의 접근 쿼리를 위해서는 HBase나 어큐뮬로 같은 스토리지 엔진이 필요하고, 분석 작업에는 HDFS, 파케이, 임팔라, 스파크 SQL, 하이브가 필요하다. 이는 결국 데이터 입수 과정과 오케스트레이션 파이프라인을 복잡하게 만든다.

입수되는 데이터에 기존 데이터를 수정하는 데이터가 포함돼 있다면 상황은 더욱 복잡해진다. 전체 데이터를 HDFS에 새로 다시 쓰거나 최신 변경분만 조회할 수 있도록 복잡한 쿼리를 사용해야 하기 때문이다. 이를 간파한 쿠두의 창시자들은 임의 접근과 순차 스캔을 모두 지원하고 기존 데이터 수정까지 허용하는 스토리지 엔진과 쿼리 엔진을 만들기 시작했다. 이를 위해서 어느 정도 성능상의 트레이드오프가 발생하기 마련이지만, 각 네이티브 기술의 성능 수중(임의 접근 읽기는 수십 밀리초 이내, 파일 스캔 속도는 초당 수배 메가바이트 수준)에 근접하는 것을 목표로 삼았다.

쿠두는 미리 정의된 스키마를 따르는 테이블에 타입을 가진 컬럼으로 구성된 레코드를 저장하는 정형 데이터 스토어다. 컬럼의 일부는 테이블의 기본 키(Primary Key)로 사용되며 레코드를 빨리 탐색할 수 있도록 인덱스가 생성된다. 쿠두는 생성, 수정, 업서트, 삭제를 지원한다. 읽기 작업에 컬럼 추출을 이용할 수 있고 컬럼 값을 기준으로 조건을 두어 필터링도 가능하다.

쿠두는 수평 파티셔닝을 통해 테이블을 클러스터에 분산 저장한다. 테이블은 두 가진 파티셔닝 메커니즘 중의 하나 또는 둘 모두를 조합하는 방법을 통해 태블릿으로 나뉜다. 레코드는 하나의 테블릿 안에만 존재할 수 있으며 기본 키 컬럼에 대해 정렬된 인덱스를 관리한다. 첫번째 파티셔닝 매커니즘은 HBase는 어큐뮬로 사용자에게 익숙한 범위 파티셔닝이다. 각 태블릿은 상한과 하한으로 이뤄진 범위를 갖고 있으며, 범위 내에 들어가는 파티션 키를 가진 모든 레코드는 해당 태블릿에 저장된다.

두 번째 파티셔닝 메커니즘은 해시 파티셔닝이다. 사용자는 테이블 파티셔닝의 기준이 되는 고정된 수의 해시 버킷을 지정할 수 있고, 각 행의 해시 값 계산에 사용되는 하나 이상의 컬럼을 선택할 수 있다. 쿠두는 각 행에서 선택된 컬럼의 해시 값을 해시 버킷의 수로 나눈 나머지를 기준으로 파티셔닝해서 해당 행을 태블릿에 저장한다.

두 가진 파티셔닝 메커니즘을 조합하면 멀티레벨 파티셔닝도 가능하다. 0 또는 그 이상의 해시 파티셔닝(복수인 경우 서로 다른 컬럼의 조합을 대상으로 해시)을 적용하고 마지막으로 범위 파티셔닝을 적용할 수도 있다. 멀티레벨 파티셔닝은 핫스팟이 발생할 수 있는 상황에 매우 유용하다. 예를 들어 시계열 기준으로 범위 파티셔닝만 적용한 경우, 시계열 데이터는 항상 맨 마지막 태블릿에만 데이터가 써지므로 맨 마지막 태블릿이 핫스팟이 된다. 해시 값이 고르게 나올 수 있는 컬럼을 기준으로 해시 파티셔닝을 추가로 적용하면 데이터 쓰기가 테이블 내 모든 태블릿에 고르게 분배될 수 있고, 각 해시 버킷을 범위에 맞게 나누는 방식으로 테이블 확장성을 확보할 수 있다.

어떤 스토리지나 쿼리 엔진을 사용하든, 적절한 스키마와 테이블 레이아웃을 선택하는 것이 효율적인 연산에 매우 중요하다. 구두도 마찬가지다. 실무자들은 서로 다른 레코드 스키마와 파티셔닝 전략에 내재된 트레이드오프를 숙지해야 용도의 맞는 최적의 조합을 만들어낼 수 있다. 쿠두의 일반적인 용도는 다음과 같다.

  • Iot 데이터셋 같은 대규모 시계열 매트릭
  • 스타 스키마 테이블에 대한 olap 분석과 같은 대규모 가변 데이터셋에 대한 리포팅ㄴ