[3부 빅데이터의 분산 처리]
3-3 데이터 마트의 구축
1) 팩트 테이블 ― 시계열 데이터 축적하기
2) 집계 테이블 ― 레코드 수 줄이기
3) 스냅샷 테이블 ― 마스터의 상태를 기록하기
4) 이력 테이블 ― 마스터 변화 기록하기
[마지막 단계] 디멘전을 추가하여 비정규화 테이블 완성시키기
[4부 빅데이터의 축적]
4-1 벌크 형과 스트리밍 형의 데이터 수집
1) 객체 스토리지와 데이터 수집 ― 분산 스토리지에 데이터 읽어들이기
2) 벌크 형의 데이터 전송 ― ETL 서버의 설치 필요성
3) 스트리밍 형의 데이터 전송 ― 계속해서 전송되어 오는 작은 데이터를 취급하기 위한 데이터 전송
4-2 [성능×신뢰성] 메시지 배송의 트레이드 오프
1) 메시지 브로커 ― 스토리지의 성능 문제를 해결하는 중간층의 설치
2) 메시지 배송을 확실하게 실시하는 것은 어렵다 ― 신뢰성 문제와 세 가지 설계 방식
3) 중복 제거는 높은 비용의 오퍼레이션
4) 데이터 수집의 파이프라인 ― 장기적인 데이터 분석에 적합한 스토리지
3부 빅데이터의 분산 처리
3-3 데이터 마트의 구축
- 데이터 웨어하우스와 데이터 마트를 구성하는 테이블
- 집계 테이블: SQL의 집약 함수를 사용해 레코드 수를 감소시킨 테이블
- 스냅샷 테이블: 마우스 정보를 정기적으로 복사한 테이블
- 이러한 테이블을 결합해 비정규화 테이블을 작성하는 것까지의 흐름
1) 팩트 테이블 ― 시계열 데이터 축적하기
- 팩트 테이블
=> 빅데이터 분석의 시작인 데이터 구조화에서의 큰 비중을 차지하는 것이 팩트 테이블
=> 팩트 테이블이 아주 작으면 메모리에 올릴 수도 있음
=> 그렇지 않으면 열 지향 스토리지에서 데이터를 압축해야 빠른 집계를 할 수 있음
- 팩트 테이블 작성방법: 추가(append), 치환(replace)
1) 추가: 새로 도착한 데이터만을 증분으로 추가
2) 치환: 과거의 데이터를 포함해 테이블 전체를 치환
- 테이블 파티셔닝: 물리적인 파티션으로 분할
=> 효율만 생각하면 추가가 압도적으로 유리하나 잠재적인 문제가 있음
1) 추가 실패를 알아차리지 못하면 팩트 테이블 일부에 결손 발생
2) 추가 잘못해 여러번 실행시 팩트 테이블 일부가 중복됨
3) 이후에 팩트 테이블 다시 만들고 싶은 경우 관리가 복잡해짐
=> 이러한 문제 발생 가능성을 줄이기 위해 테이블 파티셔닝을 사용함
* 테이블 파티셔닝: 하나의 테이블을 여러 물리적인 파티션으로 나눠 파티션 단위로 정리해 데이터를 쓰거나 삭제할 수 있도록 함
=> 1일1회, 1시간1회 등으로 자주 새 파티션을 만들어 팩트 테이블에 붙여 놓음
* 각 파티션은 매번 교체하도록 함
* 이미 존재한다면 덮어씀
2) 집계 테이블 ― 레코드 수 줄이기
- 집계 테이블
=> 데이터 양을 크게 줄이기 위해 팩트 테이블을 모아 집계한 것
=> 테이블 집계에 의해 생성된 레코드 수는 칼럼 값의 조합 수에 따라 결정되기 때문에 실제로 얼마나 줄어들지 실행하기 전까지는 알 수 없음
- 카디널리티(cardinality)
=> 각 칼럼이 취하는 값의 범위
=> 집계 테이블을 작게 하려면 모든 칼럼의 카디널리티를 줄여야 함
=> 레코드 수가 수억 건 정도라면, 집계하지 않고 MPP DB로 바로 쓰는 것도 좋을 것임
- 집계 테이블에서의 숫자 계산 시 주의!
=> 집계 테이블 작성은 다차원 모델에서 디멘전을 줄이는 것과 같은 효과가 있음
=> 단 모든 측정값이 동일하게 계산되는 것은 아니므로 주의가 필요
=> 예를 들어
1) 평균값은 집계 테이블 사용 시 '평균의 평균'과 '전체 평균'의 차이로 계산 결과가 다를 수 있음
* 집계 테이블에서 올바른 평균 산출을 위해서는 합계와 개수를 각각 측정값에 포함한 후 BI 도구 등 동적인 평균값 계산이 필요
2) 고유 수의 카운트 시에도 일일 순 사용자수에서 월간 순 사용자 수 산출은 불가함
* BI 도구로SELECT DISTINCT 사용해 중복을 제거한 작은 테이블을 만들어 순 사용자 수를 산출할 수 있음
3) 스냅샷 테이블 ― 마스터의 상태를 기록하기
- 마스터 데이터처럼 업데이트될 가능성이 있는 테이블에 대한 2가지 방안
1) 스냅샷 테이블(snapshot table): 정기적으로 테이블을 통째로 저장하는 방법
2) 이력 테이블(history table): 변경 내용만을 저장하는 방법 (모든 데이터를 스냅샷하는 것이 아닌)
- 스냅샷 테이블
=> 차후의 데이터 분석을 생각하면 스냅샷 테이블 쪽이 취급하기 쉬움
=> 마스터 테이블의 레코드 수가 많다면 스냅샷 테이블은 거대해지지만, 빅데이터 기술이므로 개의치 않아도 됨
=> 스냇샷 테이블도 시간이 지나면 커지기 때문에 일종의 팩트 테이블로 간주함
- 스냅샷 날짜에 주의!
=> 트랜잭션 데이터의 집계에서는 대부분의 경우 시간 부분을 잘라버릴 수 있음
* 1월1일의 집계 결과에 대한 마스터 데이터는 0시 시점의 스냅샷에는 아직 포함되어 있지 않아 이 시간의 차이를 고려해야 한
=> 이러한 문제의 해결을 위해서는 스냅샷을 하루의 끝에 취득하는 것으로 생각하는 방법이 있음
- 스냅샷 시에는 비정규화하기
=> 데이터 분석 시에는 결국 모든 테이블을 결합하는 것이기 때문에 처음부터 비정규화된 것이 편함
4) 이력 테이블 ― 마스터 변화 기록하기
- 이력 테이블
=> 데이터 양을 줄이는 데 도움이 됨
=> 하지만 완전한 마스터 테이블을 나중에 복원하는 것이 어려워지므로 디멘젼 테이블로는 사용하기 힘듦
* 마스터 관계의 테이블은 기본적으로 매일 스냅샷 하는 것이 좋음
- 굳이.. 이력에서 마스터 테이블 복원해야 하는 상황이라면?
SELECT * FROM (
SELECT *,
# user id별로 최신 레코드를 1로 한 후에 일련 번호를 붙임
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY date DESC) number
FROM users_history
# 최근 365일 동안의 데이터를 대상으로 함
WHERE date >= current_date() - INTERVAL '365' DAY
) t
# 번호가 1인 최신인 데이터만 조회
WHERE number = 1
[마지막 단계] 디멘전을 추가하여 비정규화 테이블 완성시키기
- 팩트 테이블과 디멘젼 테이블을 결합해 비정규화 테이블을 만듦
=> 세션 ID는 카디널리티가 크기 때문에 카디널리티가 작은 디멘젼을 만들어 결함하고, 시각화에 필요 없는 칼럼은 가급적 제거
=> 디멘젼 테이블로는 스냅샷을 사용할 뿐만 아니라 목적에 따라 각종 중간 테이블이 만들어짐
* 예를 들어, 세션당 처음/마지막 액세스 시간을 정리해 액세스 로그와 결합함으로써 처음 액세스 이후의 경과 일수 등을 알 수 있음
- 쿼리 예시
CREATE TABLE sessions AS
SELECT session id,
min(time) AS min_time, # 처음 액세스 시점
max(time) AS max_time # 마지막 액세스 시점
FROM access_log
GROUP BY session_id
- 데이터 집계의 기본형
=> 팩트 테이블에서 필요한 데이터를 꺼낼 때 시간에 의한 검색이나 참고 칼럼 수를 줄여 데이터의 로드 속도를 높일 수 있음
=> 디멘젼 테이블과 결합해 데이터 마트에 저장할 칼럼을 선택하는데 이때 가급적 카디널리티를 적게 하는 것이 중요
=> 그룹화해 측정값을 집계함으로써 비정규화 테이블을 만들 수 있음
- 데이터 집계 쿼리 예시
SELECT
# 디멘전 (일 단위로 그룹화)
date_trunc('day', a.time) time,
# 추가 디멘전 (방문한 후의 일 수)
date_diff('day', b.min_time, a.time) days,
# 측정값
count(*) count
FROM (
# ➀ 팩트 테이블로부터 필요한 칼럼만을 추출한다.
SELECT time, session_id FROM access_log
WHERE time BETWEEN TIMESTAMP '2017-01-01' AND TIMESTAMP '2018-01-01'
) a
# ➁ 디멘전 테이블과 결합
JOIN sessions b ON b.session_id = a.session_id
# ➂ 그룹화
GROUP BY 1, 2
.
4부 빅데이터의 축적
4-1 벌크형과 스트리밍형의 데이터 수집
벌크/스트리밍형의 데이터 전송
=> 데이터 수집: 수집한 데이터를 분산 스토리지에 젖아하는 프로세스
=> 해당 프로세스에서 처리 가능한 동종의 데이터를 만듦
1) 객체 스토리지와 데이터 수집 ― 분산 스토리지에 데이터 읽어들이기
- 빅데이터는 대부분 확장성 높은 '분산 스토리지'에 저장
=> 분산형 DB 이용되는 경우도 있지만, 대량 파일 저장을 위한 '객체 스토리지'를 사용
* Hadoop의 경우 HDFS, 클라우드 서비스의 경우 Amazon S3
- 객체 스토리지
=> 다수의 컴퓨터를 사용해 파일을 여러 디스크에 복사함으로써 데이터의 중복화/부하분산을 실현
* 항상 여러 디스크에 복사 -> 일부 하드웨어 고장에도 데이터 손실되지 않음
* 데이터 읽고/쓰기를 다수의 하드웨어에 분산 -> 데이터 양 늘어나도 성능 떨어지지 않음
=> 소량의 데이터일 때는 비효율적이므로 주의 필요
* 데이터양에 비해 통신 오버헤드가 너무 크기 때문임
- 데이터 수집
=> 데이터 수집: 수집 데이터를 가공해 집계 효율이 좋은 분산 스토리지를 만드는 일련의 프로세스
=> 빅데이터를 수시로 객체 스토리지에 기록하면 대량의 작은 파일이 생성돼 시간이 지남에 따라 성능을 저하시키는 요인이 됨
=> 작은 데이터는 적당히 모아서 하나의 큰 파일로 만들어 효율을 높이는 데 도움이 됨
=> 파일이 지나치게 커지는 것도 문제가 있음
* 파일 크기가 증가하면 네트워크 전송에 시간이 걸려 예상치 못한 오류 발생률도 높아짐
2) 벌크 형의 데이터 전송 ― ETL 서버의 설치 필요성
- 벌크형 방식: 전통적인 데이터 웨어하우스에서 사용됨
* DB나 파일서버/웹서비스 등에서 각각의 방식(SQL, APi 등)으로 정리해 데이터를 추출
- 데이터가 처음부터 분산 스토리지에 저장되어 있는 것이 아니라면 데이터 전송을 위한 ETL 서버를 설치
* ETL 서버에서는 구조화된 데이터 처리에 적합한 데이터 웨어하우스를 위한 ETL 도구 같은 오픈소스의 벌크 전송 도구 또는 손수 작성한 스크립트 등으로 데이터를 전송
- 벌크형 도구로 파일 사이즈의 적정화
=> 방법?
* ETL 프로세스로 정기적인 실행을 통해 그동안 축적된 데이터를 하나로 모으는 방법이 있음
* 전송하는 방법을 이용한다면 적정한 크기로 전송
=> 워크플로 관리 도구를 사용함으로써 태스크 실행을 쉽게 관리할 수 있음
* 데이터양이 많을 때는 한달/하루 단위로 전송하도록 작은 태스크로 분해해 한번의 태스크 실행이 커지지 않도록 조정
- 데이터 전송의 워크플로
=> 데이터 전송의 신뢰성이 중요한 경우 벌크형 도구를 사용
* 문제 발생 시 여러번 데이터 전송을 재실행할 수 있음
=> 벌크형 데이터 정송은 워크플로 관리 도구와 조합시켜 도입함
* 정기적인 스케줄 실행, 오류 통지 등은 워크플로 관리 도구에 맡김
3) 스트리밍 형의 데이터 전송 ― 계속해서 전송되어 오는 작은 데이터를 취급하기 위한 데이터 전송
- 메시지 배송
=> 다수의 클라이언트에서 계속해서 작은 데이터가 전송되는 방식
=> 데이터양에 비해 통신을 위한 오버헤드가 커지기 때문에 이를 처리하기 위한 높은 성능의 서버가 필요
- 보내온 메시지의 저장 방식?
=> 작은 데이터 쓰기에 적합한 NoSQL DB 이용
* Hive 같은 쿼리 엔진으로 NoSQL DB에 연결해 데이터 읽을 수 있음
=> 분산 스토리지에 직접 쓰지 않고, 메시지 큐/브로커 등의 중계 시스템에 전송
* 기록된 데이터는 일정 간격으로 꺼내고 모아 함께 분산 스토리지에 저장
- 대상별 메시지 배송
1. 웹 브라우저에서의 메시지 배송 - 웹 이벤트 트래킹
=> 자체 개발한 웹 애플리케이션 등에서는 웹서버 안에 메시지를 만들어 배송
* 전송 효율을 높이기 위해 서버상 데이터 축적해놓고 나중에 모아서 보내는 경우가 많음 (Fluentd, Logstash 같은 서버 상주형 로그 수집 SW 사용)
=> 자바스크립트를 사용해 웹 브라우저에서 직접 메시지를 보내는 방식도 있음 (= 웹 이벤트 추적)
* HTML 페이지에 태그 삽입만 하면 되므로 각종 액세스/데이터 분석 서비스 등에서 사용됨
* 수집된 데이터는 그대로 다른 서버로 전송되거나 API 경유로 함께 취득해 분산 스토리지에 저장함으로써 다른 데이터와 조합한 분석이 가능해짐
2. 모바일 앱에서의 메시지 배송 - MBaaS, SDK
=> 통신 방법이 HTTP 프로토콜을 사용하는 클라이언트 중 하나라 웹 브라우저와 동일
방법1) 서버를 직접 마련하거나 MBaaS(Mobile Backend as a Service)라는 백엔드 서비스 이용
방법2) 모바일 앱 특화 액세스 해석 서비스 통해 이벤트 데이터 수집 (모바일용의 SDK 사용해 메시지 보냄)
* 모바일 앱은 오프라인이 되는 경우도 많으므로 발생한 이벤트는 일단 SDK 내부에 축적한 뒤 온라인 상태 되었을 때 모아 보내도록 만들어져 있음
* SDK 도입 시 데이터 중복에 대한 대책 필요
3. 디바이스에서의 메시지 배송 - MQTT
=> IoT 등에서의 메시지 전달에 대한 문서 작성 시점은 표준이 없는데 그 중 하나가 MQTT
=> MQTT(MQ Telemetry Transport)
* TCP/IP 사용해 데이터 전송하는 프로토콜의 하나로 Pub(publish, 전달)/Sub(subscription, 구독)형 메시지 배송 구조를 가짐
=> MQTT 메시지 배송 방식
* 관리자에 의해 토픽 (메시지 송수신 위한 대화방 같은 것)이 만들어짐 (MQTT 브로커 = 메시지 교환 중계 서버)
-> 토픽 구독 시 메시지 도착
-> 토픽 전달 시 구독 중인 모든 클라이언트에 보내짐 (MQTT 구독자 = 메시지 수신하는 시스템)
=> MQTT 특징
* 네트워크에서 분리된 경우에도 나중에 재전송하는 구조가 프로토콜 수준에서 고려되고 있음
* HTTP에서는 애플리케이션 상에 해당 구조를 구현해야 하지만, MQTT에서는 통신 프로토콜의 표준 사양에 포함되어 있다는 의미
4-2 [성능×신뢰성] 메시지 배송의 트레이드 오프
스트리밍 형의 데이터 전송인 '메시지 배송'의 구조, 주의점
=> 메시지 배송 시스템은 성능을 우선으로 처리하기 위해 '신뢰성'을 희생시키는 경우가 있음
1) 메시지 브로커 ― 스토리지의 성능 문제를 해결하는 중간층의 설치
- 메시지 브로커 - Apache Kafka, Amazon Kinesis
=> 데이터를 일시적으로 축적하는 중산층
- 푸쉬형, 풀형 - 확장성 향상과 파일 사이즈의 적정화
=> 메시지 브로커는 데이터의 쓰기 속도를 조정하기 위한 완충 부분으로 푸쉬형에서 풀형으로 메시지 배송의 타이밍을 변환
1) 푸쉬(push)형: 송신 측의 제어로 데이터 보내는 방식 (=> 생산자(producer): 메시지 브로커에 데이터를 넣는 것)
* 메시지 브로커에 집중시키고 일정한 빈도로 꺼낸 데이터를 분산 스토리지에 기록해 성능 문제를 피할 수 있음
2) 풀(pull)형: 수신 측의 주도로 데이터를 가져오는 방식 (=> 소비자(consumer): 꺼내오는 것)
- 메시지 라우팅
=> 스트림 처리: 짧은 간격으로 차례대로 데이터를 꺼내서 처리하는 것
=> 메시지 라우팅: 메시지 브로커에 써넣은 데이터는 복수의 다른 소비자에게서 읽어 들일 수 있는데, 이를 통해 메시지가 복사되어 데이터를 여러 경로로 분기시킬 수 있음
2) 메시지 배송을 확실하게 실시하는 것은 어렵다 ― 신뢰성 문제와 세 가지 설계 방식
- 다음 중 하나를 보장하도록 설계
1) at most once: 메시지는 한 번만 전송 (단, 전송 실패로 결손 발생 가능)
=> 그러나 대개는 데이터의 결손을 피하고자 재전송이 이루어짐
2) exactly once: 메시지 손실/중복 없이 한 번만 전달
=> 네트워크상에서 분단된 2개의 노드가 있는 경우 양쪽의 통신 내용을 보장하려면 그 사이에 중계하는 '코디네이터'가 필수적
=> 2가지 문제점
문제1) 분산 시스템에서는 코디네이터가 항상 존재한다고 가정할 수 없음
문제2) 성능상의 문제로 코디네이터의 판단에만 따르고 있으면 시간이 너무 소요됨
=> 따라서 메시지 배송 시스템에서는 코디네이터를 도입하지 않고 at least once로 메시지 중복 가능성을 고려해 시스템을 구축함
3) at least once: 메시지는 확실히 전달 (단, 중복 가능성 있음)
=> 중복 제거: 메시지가 재전송되어도 이를 없애는 구조만 있으면 보기에 중복 없는 것처럼 보이게 할 수 있음
=> 주의할 점은 대부분의 메시지 배송 시스템은 at least once를 보장하지만, 중복 제거는 이용자에게 맡기고 있어 TCP/IP처럼 자동으로 중복을 제거해주지 않음
3) 중복 제거는 높은 비용의 오퍼레이션
- 오프셋을 이용한 중복 제거
=> 각 메시지에는 오프셋(시작 위치)을 덧붙임
=> 벌크형 데이터 전송 같이 데이터양이 고정된 경우에 잘 작동함
- 고유 ID에 의한 중복 제거
=> UUID 등 고유 ID를 지정
=> 현실적으로는 최신에 받은 ID만 기억해두고 그보다 늦게 온 메시지의 중복은 허용하는 방식으로 함
- 종단간(End to End) 신뢰성
=> 빅데이터의 메시지 배송에서는 종종 신뢰성보다 효율을 중시하므로 중간 경로에 at least once를 보장하지만 중복 제거는 하지 않는 것이 표준적인 구현임
=> 신뢰성 높은 메시지 배송을 실현하려면 중간 경로를 모두 at least once로 통일하고 클라이언트 상에서 모든 메시지에 고유 ID를 포함하도록 하고 경로의 말단에서 중복 제거를 실행
4) 데이터 수집의 파이프라인 ― 장기적인 데이터 분석에 적합한 스토리지
- 데이터 수집의 파이프라인
=> 데이터를 구조화해 열 지향 스토리지로 변환함으로써 장기간의 데이터 분석에 적합한 스토리지로 완성시키는 과정
- 중복을 고려한 시스템 설계
=> 스트리밍형 메시지 배송에서는 중간에 명시적으로 중복 제거 방식을 도입하지 않는 한 항상 중복의 가능성이 있음
* 매우 높은 성능의 시스템이 필요하므로 아주 작은 중복은 무시하는 경향이 있음
=> 따라서 신뢰성이 중시되는 경우 스트리밍형 메시지 배송을 피하는 것이 가장 좋음
'개발독서 > 데이터' 카테고리의 다른 글
빅데이터를 지탱하는 기술 (5.2~5.3 배치/스트리밍 형의 데이터 플로우) (0) | 2024.12.21 |
---|---|
빅데이터를 지탱하는 기술 (1.3~1.5 스크립트 언어에 의한 특별 분석과 데이터 프레임 & BI 도구와 모니터링) (0) | 2024.10.13 |
데이터 중심 애플리케이션 설계 (7장 트랜잭션) (0) | 2024.06.24 |
데이터 중심 애플리케이션 설계 (6장 파티셔닝) (0) | 2024.06.09 |
데이터 중심 애플리케이션 설계 (5장 복제) (0) | 2024.06.03 |