brunch

You can make anything
by writing

- C.S.Lewis -

by 정주홍 Jun 26. 2018

번역: Hadoop에서 업데이트를 하는 법

Hadoop에서 어떻게 데이터 업데이트를 할 수 있는가

Hadoop HDFS는 WORM(Write Once Read Many), 불변성(Immutability)을 가진 파일 시스템이다. 한 번 write 한 데이터를 여러 번 읽어서 사용하고, 파일 내용을 수정할 수 없는 특성을 가졌다는 뜻이다. 이런 특성을 가진 탓에 데이터 업데이트가 쉽지 않다. 관련 자료를 찾아 공부하던 중 꽤 잘 정리된 글을 찾아 번역하기로 했다. 읽기 편하도록 다소 많은 부분을 의역했으니 참고 바란다.


Hadoop: 업데이트 연산 없이 업데이트를 하는 법

불변성의 파일 시스템(Immutable file system)과 업데이트 명령어가 없는 Hadoop에서 어떻게 업데이트를 할 수 있을까? 모든 Hadoop 프로젝트에서 발생하는 문제이다. Hadoop으로 데이터를 쌓는다는 것은 보통 새로운 이벤트나 트랜잭션 데이터를 append 하는 것이다. 그러나, 분석을 위해서는 쌓았던 데이터를 또 다른 데이터(이 글에서는 차원 데이터 dimension data라고 한다)와 join 하여 사용해야 하는데, 차원 데이터는 시간이 지남에 따라 바뀌는 경우가 많다.

웹사이트 행동 데이터를 수집하는 경우를 생각해보자. 이벤트들은 JSON 데이터로 생성된다. 스트리밍으로 수집하든, 마이크로 배치로 수집하든 어쨌든 HDFS에 append 할 것이다. 이제 비즈니스 사용자가 그 이벤트, 특히 회원 등록 시스템에서의 데이터를 다양한 속성에 따라 분석하길 원한다고 가정하자. 회원 등록을 위한 속성들은 third party 시스템에서 제공되기도 하고, 시스템에 직접 등록되기도 한다. 어쨌건, 현재의 회원 정보 혹은 회원 정보 변경 내역을 Hadoop에서 관리하여야 한다.

Hadoop에서의 데이터 업데이트는 어려운 일이다. 최근 프로젝트와 과거 오랜 기간 동안 Pig 스크립트 작성 일을 해오면서 나의 동료 중 하나는 이런 말을 한 적이 있다: "골치가 아파. 데이터베이스 쓸 때는 생각할 필요도 없던 것들을 신경 써야 해." 차원 데이터를 ETL 도구들과 RDBMS로 다뤘던 그의 경험은 훌륭했지만, 이제는 그러한 일들을 Hadoop 위에서의 데이터 프로그래밍으로 할 수 있어야만 했다. 비교하여 말하자면, Hadoop에서의 데이터 프로그래밍은 아직 완전하지 않고, 나아져가는 중이다.

만약 당신이 upsert(merge) 로직이나 타입 2 차원 데이터의 로직을 Hadoop 상에서 구현하고 싶다면, 그걸 직접 구현해야만 한다. (역주: 타입 2 차원 데이터가 무엇인지는 다음 링크 참고) HDFS에 저장된 파일은 불변적이므로 파일이 한 번 쓰이고 나면 수정될 수 없다. 매우 최근에 나타난 Hive도, Pig도 업데이트 명령을 지원하지 않는다. 언젠가는 지원하는 날이 오겠지만, 현재로서는 업데이트 작업을 위해 별도 알고리즘을 개발해야 한다. 다행히도 업데이트 작업에는 몇 가지 패턴이 있다.

이 글에서는 4가지 접근 방법을 다룰 것이다: 하이브리드 업데이트, HBase 차원 저장, 병합 및 압축 업데이트, 그리고 Good Enough 업데이트이다. 다섯 번째 방법으로 추가적인 레이어를 둬서 읽기 시간에 업데이트를 해결하는 방법이 있다. 이 방법은 필요한 데이터에 대해서만 배치 처리를 하지 않고 전체를 처리하기 때문에 매우 많은 용량을 요구한다. 이 다섯 번째 방법은 이 글에서 다루지는 않을 것이다.


하이브리드 업데이트(Hybrid Update)

하이브리드 업데이트 접근은 ETL 도구와 SQL 프로그래밍으로 RDBMS에서 차원 데이터를 다루고, 그 데이터를 Sqoop과 같은 도구를 이용하여 HDFS에 복사하여 이전의 데이터를 교체하는 접근 방법이다. 특별한 업데이트 규칙이나, 천천히 변화하는 차원, surrogate key 할당과 같은 일들은 생산성 높은 ETL 프로그래밍 언어로 관리될 수 있다. Hadoop에서 업데이트는 파일을 변경하는 것이다. 많은 회사들이 차원 데이터를 유지하기 위해 데이터 웨어하우스에 투자를 했기 때문에 이 방법은 가장 구현하기 쉽고, 첫 번째로 적용될 수 있다.

그러나 불리한 점도 있다. 먼저, 이 방법은 확장성이 없다. 만약 10억 개의 행을 가진 차원 데이터라고 생각해보자. 하이브리드 업데이트 방법을 적용하려면 Hadoop 상의 데이터를 업데이트하려고 할 때마다 10억 개의 레코드들을 매번 RDBMS에서 추출해와야 한다. 지금 당장은 다룰만한 데이터 크기 일지 몰라도, 데이터가 점점 늘어남에 따라 다루기 어려운 크기가 될 것이다.

두 번째로, 이 접근 방법은 다른 환경에서 실행되는 프로세스들 간의 조정을 필요로 한다. RDBMS에서 데이터 추출이 시작되기 전에 먼저 ETL 프로세스가 완료되어야만 한다. Hadoop 기반의 다른 작업들은 RDBMS에서의 작업과 데이터 추출이 완료되기 전에는 시작될 수 없다. Pentaho와 같은 도구를 써도 한계가 있다.

결국, 차원 데이터가 작고 데이터가 천천히 늘어나는 경우에는 하이브리드 업데이트가 적합한 방법이다. 하지만, 다른 세 가지 접근 방법을 이용하기 전 임시로 사용할 방법으로서만 추천한다.


HBase 차원 저장(HBase Dimension Store)

HBase는 Hadoop 위에서 돌아가는 NoSQL 데이터베이스이다. HBase는 업데이트를 지원하기 때문에 차원 데이터를 관리하기 위한 솔루션으로 고려되곤 한다. 사실, 많은 ETL 도구들은 내부적으로 HBase를 이용한 삽입, 업데이트, 제거 단계를 가진다. 실시간 데이터의 경우에도 HBase를 이용하면 빠르게 처리할 수 있다. 또한, HBase의 테이블은 Hive나 Impala와 같은 도구를 통해 SQL로 다룰 수 있다. 이러한 점들이 HBase에 끌리게 만들지만, HBase를 사용하는 경우 성능을 충분히 끌어올리기 위한 작업들이 필요할 수 있다.

하나의 행 혹은 범위적으로 행이 선택될 수 있는 경우 HBase에서 잘 작동한다. 만약 전체 테이블을 스캔해야 하는 질의(분석 질의일 경우 종종 이렇다)의 경우 HBase는 비효율적이다. 우리 연구실에서는 Impala를 이용하여 파일 기반의 차원 데이터와 HBase를 사용하는 것에 대해 실험을 해봤다. 모든 경우에서 파일 기반의 차원 데이터를 다루는 것이 HBase를 이용하는 것보다 매우 나은 성능을 보였다.

만약 당신이 이미 HBase를 다루고 있고, 자원을 다른 Hadoop 클러스터에 잘 공유할 수 있고, HBase 질의가 항상 하나의 행 혹은 범위적으로 행을 반환한다면 HBase 차원 저장 방법은 좋은 해결 방법이 될 것이다. 그렇지 않다면 다음의 두 가지 접근 방법을 권한다.


병합 및 압축 업데이트(Merge and Compact Update)

하이브리드 업데이트와 HBase를 이용하는 접근 방식과 달리 이 업데이트 전략은 특정 기술을 이용하기보다 알고리즘적인 방법이다. 알고리즘은 Java MapReduce, Pig, Hive, Spark, Cascading, Pentaho Visual MapReduce를 비롯한 어떠한 Hadoop 데이터 프로그래밍으로든 구현될 수 있다. 알고리즘은 다음과 같은 단계로 구성된다.

1. 현재 버전의 마스터 데이터를 둔다: 차원 데이터 전체가 HDFS에 존재해야 한다. (역주: 사실 Hadoop과 연동되는 어떠한 분산 파일 시스템이든 상관없이 durable 한 파일 시스템이면 된다. 예를 들면 S3도 가능.) 혼동되지 않도록 이를 마스터 데이터라고 명한다. 처음 데이터를 로드할 때 한 번이면 된다.

2. 델타 데이터를 로드한다: 새로 업데이트된 데이터를 HDFS에 로드한다. 이를 델타(변경된) 데이터라 한다. 

3. 데이터를 병합한다: 비즈니스 키 필드를 이용하여 마스터 데이터와 델타 데이터를 join 하여 합친다.

4. 데이터를 압축한다: 데이터를 병합하고 나면 각 비즈니스 키마다 하나 이상의 레코드가 존재하게 될 것이다. 압축 단계에서 하나 혹은 그 이상의 레코드들이 나오도록 압축한다. (역주: Deduplication 로직을 적용한다고 생각하면 이해하기 쉽지만 엄밀히 말하면 적절하지 않은 표현일 수 있다.) 가장 쉬운 사례로는 update timestamp가 가장 나중인 것을 선택하는 로직을 생각해볼 수 있다. 그러나, 만약 레코드 버전이 유지되어야 한다면 여러 버전의 레코드가 출력되도록 로직을 짜야할 것이다. 비즈니스 로직 특성상 더 복잡한 로직이 필요할 수 있다. 예를 들어, 어떤 필드는 절대 업데이트되지 않기를 바랄 수도 있고, 어떤 필드는 특정 조건에만 업데이트되길 원할 수도 있다.

5. 압축 결과를 임시 저장소에 저장한다: 대부분의 Hadoop job들은 이미 존재하는 디렉토리를 덮어쓸 수 없기 때문에 압축 작업에 대한 임시 출력 데이터를 따로 저장해야 한다.

6. 원본 마스터 데이터를 덮어쓴다: 임시 출력 데이터를 원본 데이터 저장 위치로 이동시켜 이전의 버전을 덮어쓴다.

Hortonworks의 블로그 글에서 Hive를 이용한 방법을 잘 설명했다. 글에서 소개한 방법은 압축 단계에서 복잡한 비즈니스 규칙이 없을 경우 잘 작동한다. 만약, SQL로 구현하기 어려운 비즈니스 규칙이 요구된다면 Hive를 이용하는 것은 어려울 것이다.

이 방법의 단점은 단 하나의 레코드를 업데이트하고 싶더라도 전체 차원 테이블을 로드해야 한다는 점이다. 차원 테이블이 2TB의 크기라면 업데이트를 하려고 할 때마다 2TB를 읽고 써야 한다는 것이다. 당연히 그런 것은 원치 않을 것이다. 다음 접근 방법을 알아보자.


Good Enough 업데이트

매우 큰 차원 데이터여서 배치 윈도우상으로 처리될 수 없다면 더 큰 클러스터를 운영하거나, 다른 접근 방법이 필요하다. 바로 Good Enough 업데이트 접근 방법이다. (역주: 적절한 번역이 떠오르지 않아 원문 용어를 그대로 사용)

이 방법의 핵심은 파티셔닝 전략을 잘 짜서 차원 데이터 업데이트 시 전체가 아닌 일부 파티션만 업데이트하도록 하는 것이다. 레코드가 생성된 시간 혹은 마지막으로 수정된 시간을 이용하여 최근 추가된 데이터가 변경 가능성이 높다는 가정을 이용하기도 한다. (타입 2 차원 데이터의 경우, 과거 버전의 데이터는 변경할 수 없으므로 현재 버전의 데이터를 과거 버전의 데이터와 독립시키길 원할 것이다.) HDFS에서는 디렉토리를 나누는 것으로 파티션을 분리한다. Hive를 비롯한 여러 솔루션들에서는 파티션을 인식하여 질의 성능을 향상하여준다.

이러한 파티셔닝 전략이 있음으로써 변경됐을 것이라 판단되는 파티션에 대해서만 병합 및 압축 업데이트 방법을 적용할 수 있다. 중복된 차원 레코드들을 삽입할 위험이 있으므로 전체 데이터를 처리하지 않는다. 중복된 차원 레코드들은 변경된 델타 레코드들이 이전 버전의 파티션을 찾지 못했을 때 발생한다. (역주: 중복된... 부분은 잘 이해가 안 됨.)

질의 결과 같은 차원 키에 대해 하나 이상의 차원 레코드가 나타날 수 있는데, 이것은 차원 키가 잘못 선택됐다는 뜻이다. 이런 상황은 전체 데이터에 대해 병합 및 압축 업데이트를 하지 않는 한 언제나 발생할 수 있는 문제이다. 그렇기 때문에 주중에는 Good Enough 업데이트 방법을 적용하다가 주말에는 전체 업데이트를 적용하는 전략을 취하기도 한다. 또는, 데이터의 질을 관리하는 질의를 수행하여 중복을 찾고, 문제가 발견됐을 경우에만 병합 및 압축 작업을 실행하는 곳도 있다. 어쨌건 데이터 정합성이 맞지 않는 때가 존재할 순 있지만, 결국 해결될 것이다.

Good Enough 업데이트 접근 방법은 전체 데이터에 대해 병합 및 압축 업데이트를 하기 어렵고, 데이터에 에러가 존재하는 것이 어느 정도 허용되는 경우에만 선택되어야 한다. 이벤트/서브 트랜잭션 데이터 수집 상에 애초에 문제가 존재할 수 있다는 특성상 Hadoop 기반 분석은 대체로 그러한 데이터 에러가 허용되는 경우가 많다.


적절한 업데이트 전략 선택하기

예상했듯이, 만병통치약은 없다. 아마 하나 이상의 업데이트 접근 방법을 이용할 것이다. 어떤 상황에서 어떤 접근 방법이 적절한지 판단하기 위해 4가지 주요 고려 사항이 있다. 먼저, 차원 데이터의 크기가 어느 정도인가? 얼마나 많은 레코드들로 이뤄진 차원 데이터인가? 두 번째로, 업데이트가 얼마나 천천히 이뤄져도 상관없는가? 배치 윈도우를 갖고 있는지, 스트림처럼 업데이트를 바로 확인할 수 있어야 하는가? 세 번째로, 데이터가 얼마나 잘 변경되는가? 새로운 데이터가 추가되기만 하거나 일부만이 변경돼서 거의 잘 바뀌지 않는가, 혹은 굉장히 많은 레코드들이 변경될 수 있는가? 네 번째로, 업데이트 규칙이 얼마나 복잡한가? 이전 버전의 레코드가 덮어씌워지기만 하면 되는가, 아니면 타입 2 차원 데이터처럼 추가적인 업데이트 규칙이나 의존성을 가지는가? 다음의 테이블을 참고하여 업데이트 전략을 고려해보기 바란다.

보통, 복잡한 업데이트 규칙을 갖고 있지만 크기가 작은 차원 데이터의 경우 하이브리드 업데이트 방법이 유용하다. 업데이트를 빠르게 확인할 수 있어야만 한다면 HBase를 이용하는 것이 낫다. 병합 및 압축 방법은 많은 경우에 유용하지만 확장성이 떨어지고 업데이트가 오래 걸린다는 한계가 있다. Good Enough 방법은 병합 및 압축 방법의 한계를 어느 정도 극복하는데 도움이 되지만, 데이터 질이 떨어질 수 있다는 잠재적 문제가 있다.


마치며

Hortonworks는 Stinger.next의 일부로 Hive의 배치 삽입, 업데이트, 그리고 삭제 기능을 포함시켰다. 이런 기능은 큰 규모의 삽입, 업데이트, 삭제를 위해 각 단일 레코드에 대한 트랜잭션이 일어나지 않도록 설계된 기능이다. 너무 많은 트랜잭션은 성능 저하를 일으키기 때문이다. 더불어, Hive에서 제공되는 ACID 트랜잭션은 ORC 포맷의 파일에 대해서만 작동하고 아직 발전해야 할 것이 많은 기능이다. 이런 기능들이 더 많이 발전해서 앞으로 이용할 수 있게 되길 바란다.

정주홍 소속 직업개발자
구독자 338
작가의 이전글 빅 데이터 프레임워크, 솔루션들의 목적과 역할

매거진 선택

키워드 선택 0 / 3 0
브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari