[데이터 엔지니어링 디자인 패턴] 02. 데이터 수집
[데이터 엔지니어링 디자인 패턴] 02. 데이터 수집
데이터 엔지니어링 디자인 패턴 시리즈의 글입니다.
데이터 엔지니어링 시스템의 첫단계는 다양한 data producer에게서 데이터를 수집하는것.
수집한 데이터를 통합할때 마주할수잇는 시나리오와 챌린지를 알아보자
2.1 전체 적재
- 매번 데이터셋 전체를 로드하는 데이터 수집 시나리오
- db bootstrap , reference dataset 등의 상황에 유용
전체로더
가장 간단한 패턴 - 추출(extract)과 적재(load) 2단계로 구성
- 문제
- 마지막 데이터 수집 이후 변경된 레코드를 감지하는데 도움이 되는 속성이 없다면?
- 해결책
- 전체로더 패턴이 이상적인 해결
- 동종DB간 : EL(extract-load)
- 이종DB간 : ETL (extract-transform-load)
- 결과
- 데이터 크기
- 일정한 스케쥴에 따라 실행되는 배치잡이므로, 데이터셋이 천천히 증가한다면 일정한 컴퓨팅 자원으로 오랫동안 문제없이 작동
- 그러나 동적으로 진화하는 데이터셋의 경우, 정적 컴퓨팅 자원으로는 문제가 생길 수 있음
- autoscaling기능을 활용해야함
- 데이터 일관성
- 전체로더는 일관성을 잃을 위험이 있음
- drop-and-insert 작업으로 해도, 조회와 수집이 동시에 실행되면 데이터 일관성이 깨질 위험 존재
- transaction으로 통해 동시성 문제 해결 가능
- view같은 single data exposition abstraction(단일 데이터 노출 추상화)로 해결 가능
- 예상치 못한 문제 발생시 이전 버전의 데이터셋을 사용해야할수있음
- delta lake, big query등에서 제공하는 tiem travle 기능 사용
- 혹은 view로 직접 해당 기능 구현
- 데이터 크기
2.2 증분 적재
- 지속적으로 증가하는 데이터셋에 적합
- 물리적/논리적으로 나누어진 데이터셋의 더 작은부분을, 더 자주 수집
증분 로더
데이터셋의 새로운부분을 처리
- 문제
- 데이터 크기가 계속 증가하되,
- 불변하는 이벤트를 수집해야함
- 해결책
- 지속적으로 증가하는 데이터셋 = 증분로더 패턴이 이상적인 해결
- delta column을 사용하여 마지막 실행 이후 추가된 레코드를 식별
- time-partitioned dataset에 의존하여, 수집할 레코드 집합을 감지
- 결과
- 물리삭제 (hard delete)
- 원본에서 레코드를 물리적으로 삭제했어도, 컨슈머에서는 여전히 데이터가 남아있을수 있음
- 삭제된 레코드에는 delta column이 존재하지않음
- 논리삭제(soft delete)를 사용해서 해결 가능
- back-filling
- back-filling은 언제든지 발생 가능한데, 증분로더에서는 전체적재를 수행하기때문에 꽤 나쁜영향을 줄수있음
- 수집 window를 제한하여 문제를 완화할수있음
- 물리삭제 (hard delete)
변경 데이터 캡쳐
CDC (change data capture) 데이터 수집 지연시간이 더 낮아야하거나, 물리삭제를 지원할 기능이 필요하면 적합
- 문제
- 테이블 변화를 30초 내에 캡쳐하고
- 중앙 토픽에서 다른 컨슈머들에게 제공해야함
- 해결책
- CDC 패턴이 이상적 해결
- 수정된 모든 레코드를 직접 수집
- 더 빠른 레코드 접근이 가능
- 더 낮은 지연시간 보장
- 물리삭제를 포함한 모든 유형의 데이터 작업을 commit log로 가로챔
- 논리삭제를 사용할 필요가 없음
- 결과
- 복잡성
- 서버 커밋로그 활성화 등, 운영팀의 도움이 필요
- 데이터 범위
- 보통 프로세스 시작 이후에만 변경사항 적용 가능
- payload
- 작업유형 (update, insert, delete) , 컬럼유형 등의 부가적 메타데이터 제공
- consumer는 관련없는 속성은 무시하도록 처리 로직을 짜야함
- data semantic
- 저장상태의 데이터를 수집
- 따라서 정적인 레코드들은 “이동중인 데이터”가 됨
- ex. join작업이 타이밍에 따라 성공할수도/실패할수도 있음
- 복잡성
2.3 복제
- 목표 데이터를 있는 그대로 한 장소에서 다른 장소로 복사
- 이상적인 환경에서만 가능
- 실제로는 컴플라이언스 규정으로 인해 입력 데이터를 변경하는 경우가 많음
패스스루 복제기
passthrough replicator 보통 데이터업체가 해당 모드를 제공함
- 문제
- 외부 api를 통해 데이터를 적재할때, 이는 멱등성을 갖지 않을 수 있음
- 하루 중 시점에 따라 서로 다른 결과가 반환될 수 있음
- 해결책
- 환경간 일관성이 필요하다면, passthrough replicator 패턴이 적합
- EL(extract-load) job에 의존
- 레코드를 있는 그대로 복제
- 그렇지않으면, 데이터 품질문제를 일으킬수있음
- ex. 날짜 유형 변환 이슈
- ex. 부동소수점 반올림 이슈
- replication policy 를 기반으로, 데이터 스토어 제공업체가 복제하도록 세팅해야함
- 결과
- 핵심은 구현을 단순하게 유지하는것
- json i/o api에 의존하는것을 피하자
- 원시텍스트 api 사용 권고
- 보안과 독립성
- 통신은 항상 까다로움
- 버그가있으면 오류가 날수있음
- pull보다 push접근방식으로 복제 구현
- pii data
- 변환 복제기 패턴 사용
- 해당 속성을 제거하기 위한 변환단계 추가
- latency
- 인프라기반 구현은 추가적인 지연이 종종 발생
- SLA를 확인해서 수용할만한지 검토 필요
- metadata
- 중요함
- 파티션 내 헤더와 이벤트 순서에도 신경써야함
- 핵심은 구현을 단순하게 유지하는것
변환 복제기
transformation replicator
- 문제
- 운영 > 스테이징 데이터복제가 필요한데, pii 데이터가 포함될때
- 단순 passthrough job은 사용 불가
- 해결책
- spark, flink등의 처리 프레임워크에서 사용자 정의 매핑함수 사용
- sql select문으로 변환
- 결과
- 텍스트 파일 형식의 변환 위험
- ex. 날짜시간형식이 표준과 다를수있음
- job실패시 불필요한 작업 초래
- 따라서, ‘단순하게 유지’해야함
- ex. 타임스탬프 컬럼을 그대로 정의하기보다 문자열로 설정
- 비동기화
- pii - 새 속성이 나타날수도, 아니던게 pii로 분류될수도있음
- 데이터 거버넌스 도구 필요
- 변환 로직 자동화, or 직접 규칙 구현
- 텍스트 파일 형식의 변환 위험
2.4 데이터 컴팩션
- 데이터셋은 시간이 지나며 데이터가 유입되어 규모가 커지면 병목현상이 일어날 수 있음
- 특정 시점에 이르면 파일을 나열하는것과 같은 메타데이터 관련작업이 데이터 처리 변환보다 더 오래걸리게됨
컴팩터
기본 파일의 스토리지의 용량을 줄여야함 → 컴팩터 패턴
- 문제
- 10분이내에 배치가 끝나야하는 job
- 작은 파일들이 너무 늘어나 메타데이터 오버헤드가 생김
- 실행시간의 70%가 처리할 파일 sort에 사용됨
- 시간지연, 서비스비용에 악영향
- 해결책
- 대량의 작은 파일을 저장하면 i/o 비용이 커지고 파일 여닫는 시간이 길어짐
- ⇒ compactor (여러개의 작은 파일을 큰 파일로 결합) 하여 문제 해결
- 조회시 전체적인 i/o 비용을 줄임
- 구현
- 오픈테이블파일 형식 - 트랜잭션 분산 데이터 처리 잡을 실행해 큰 파일로 병합
- iceberg : rewrite data file 작업
- delta lake :
OPTIMIZE명령어 - apache hudi : MoR 테이블 설정 (데이터를 수정할때 변경분을 따로 저장한 뒤, 읽을때 merge해서 보여주는 방식)
- 데이터 스토어 - 단순 설정으로 컴팩션 실행
- kafka : 설정된 빈도에 따라 데이터 스토어가 컴팩션 프로세스 관리
- 오픈테이블파일 형식 - 트랜잭션 분산 데이터 처리 잡을 실행해 큰 파일로 병합
- 결과
- 비용 vs 성능 trade-off
- compaction은 연산집약적인 job
- 파이프라인 외부에서, 드물게 실행하는것을 추천
- compaction 되기 전에 잡이 실행되면 최적화 기술을 활용하지 못해 문제가 될 수 있음
- 비용과 성능이 완벽할수 없음을 이해해야함
- 일관성
- compaction은 이미 존재하는 데이터를 rewrite하는것
- consumer는 사용할 데이터와 컴팩션 중인 데이터를 구별하기 어려울 수 있음
- json, csv같은 원시파일보다 delta lake나 iceberg같은 ACID 속성을 가진 개방형 테이블 파일 형식에서 훨씬 간단하고 안전함
- 정리
- compaction job은 원천 파일을 보존
- 작은파일이 여전히 존재하므로, 점유한 공간을 회수하기 위한 clean job을 완성해야함
VACUUM등의 명령어로 정리 가능
- 비용 vs 성능 trade-off
2.5 데이터 준비
언제 데이터 수집을 시작해야 할까?
준비 마커
readiness marker(준비 마커)는 적절한 시점에 수집을 시작하게하는 유용한 패턴 데이터셋의 완전성을 보장하도록 데이터를 수집
- 문제
- 데이터를 사용하는 팀들이 잡 완료시점을 몰라, 불완전한 데이터셋을 수집하게됨
- 데이터 소비가 가능한 시점을 알려주는 메커니즘을 달라고 요청함
- 해결책
- readiness marker패턴을 사용해 데이터셋을 처리 준비 상태로 표시 가능
- 구현
- 이벤트를 사용해 데이터셋의 완전성을 알림.
- 데이터 생성이 성공한 후 생성된 flag file로 구분
- spark, delta lake 등 데이터 처리계층에서 사용 가능
- 혹은 데이터 ochestration계층에서 데이터 처리 후 별개 작업으로 플래그 생성
- 파티션된 데이터에서는 관례적으로 준비마커를 생성
- 파티션 10을 처리하고싶다면, 파티션11작업을 시작하기까지 기다려야함.
- 이벤트를 사용해 데이터셋의 완전성을 알림.
- 결과
- 독자가 데이터 검색과정을 제어하는 pull 접근방식을 사용
- 강제성 부족
- 지연 데이터에 대한 신뢰성
- 파티션 기반 구현 시 데이터 지연이 필연적으로 발생
- 파티션을 한번 닫히면 절대 변하지 않을 불변의 영역으로 간주하거나
- 변경조건을 명확히 정의하고 컨슈머와 공유해야함
2.6 이벤트 주도
- 이상적인 수집 시나리오에서는 새로운 데이터셋을 정기적으로 이용
- 비이상적인 시나리오에서는 데이터가 들어오는 빈도를 예측하기 어려움
- 정적인수집 → 이벤트 기반 수집으로 사고방식 전환 필요
외부 트리거
readiness marker pattern은 새로운 데이터 확인할 책임이 컨슈머에있는 pull semantic event-driven 데이터셋은 프로듀서가 알려주는 push semantic을 선호
- 문제
- 새롭게 처리할 기능이 있을때만 파이프라인을 처리하고싶다
- 중앙 메시징 버스로 알림이벤트를 보낼수 있는 상태라, 이를 활용하고자 함
- 해결책
- 데이터 생성이 예측 안될때가 많음
- 보통 짧은 interval의 cronjob으로 해결 가능하나,
- 컴퓨팅 자원 낭비 / 불필요한 운영 부담
- ⇒ external trigger pattern (event-driven) 사용이 적합
- 세가지 주요작업으로 구성됨
- 알림 채널 구독
- 알림에 반응
- 데이터 오케스트레이션 or 데이터 처리 계층에서 수집 파이프라인 trigger
- 결과
- push vs pull
- pull : 처리할 이벤트가 있는지 지속적으로 확인
- push : producer가 알리지 않는 한 아무것도 하지 않음
- 실행 context
- external trigger가 단순 오케스트레이터 엔드포인트를 호출하는 ping 메커니즘이 될 위험이 있음
- 트리거된 파이프라인은 유지관리 되어야함을 명심
- 트리거링 호출에 적절한 meta data 정보를 강화하는게 좋음
- trigger job version
- alert envelope
- 처리에 걸린 시간
- 이벤트 시간
- ⇒ 장애 발생 원인 파악에 유용
- 오류관리
- 실패 트리거 설계시 이벤트 유지를 목표로 해야함
- push vs pull