데이터 엔지니어링 아키텍처
왜 운영시스템과 분리해서 데이터 웨어하우스를 만들어야 하나요?
-> 운영시스템은 OLTP라고 하고, 분석시스템은 OLAP라고 하는데 이 둘간의 목적이 다르기 때문입니다.
OLTP
OLTP는 일상적인 업무 처리가 주된 목적입니다. 예를 들어, 주문, 결제, 회원가입과 같은 작업에서 데이터 삽입(INSERT), 수정(UPDATE), 삭제(DELETE)가 빈번하게 발생합니다. 이때는 단일 트랜잭션의 빠른 처리 속도가 가장 중요합니다.(스피드가 생명) 또한 데이터의 중복을 최소화하고 일관성을 유지하기 위해 정규화된 스키마 구조를 가집니다.
OLAP
반면, OLAP는 데이터 분석에 초점을 맞춥니다. 매출 분석이나 시장 트렌드 파악처럼 대량의 데이터를 집계하고 분석하는 것이 목표입니다. OLAP 시스템은 빠른 데이터 읽기를 위해 비정규화된 스키마 구조를 가지는 경우가 많으며, 읽기 중심의 복잡한 쿼리에 대해 빠른 응답 시간이 중요합니다.
즉, 운영은 스피드가 중요하고, 분석은 좀 깊이 있게 해야 하니까 서로 방해하지 않도록 분리해야 하는 것입니다.
만약 운영 DB에 지난 1년치 전체 매출 분석을 위한 무거운 분석 쿼리를 날리면 시스템 전체가 늘어져서, 정작 중요한 실시간 거래 처리에 영향을 줄 수 있습니다.
DW - 데이터 웨어하우스
이때 DW는 OLAP가 가능하게 해주는 데이터창고 입니다.
DW를 구축해두면 분석가들이 DW를 통해 마음놓고 쿼리를 날려 분석할 수 있습니다.
또한 DW는 여러 시스템에 흩어져 있는 데이터를 한 곳에 모아서 이렇게 통합해놓고, 분석에 필요한 과거 이력 데이터, 히스토리 데이터를 쌓아서 보존해 줍니다
사람들이 많이 사용하는 플랫폼으로는 BigQuery, Snowflake, Databricks, AWS Redshift가 있습니다.
차원 테이블과 사실 테이블
백엔드 개발자가 MYSQL과 같은 DB를 모델링 하는것처럼 데이터 엔지니어는 DW를 차원 모델링을 합니다.
DW는 정형데이터이기 때문에 분석 쿼리 성능을 높이기 위해서는 차원 모델링을 해야합니다.
또한 백엔드 개발자는 바로 테이블을 설계하면 되지만 데이터 엔지니어는 차원 테이블과 사실 테이블로 구분해서 설계해야합니다.
- 차원 테이블은 ‘누가, 언제, 어디서, 무엇을’과 같은 분석의 속성을 담는 테이블입니다.
- 사실 테이블은 ‘얼마나 많이, 몇 개’와 같은 분석의 측정 지표수치를 담는 테이블입니다.
예를 들어 “매출액, 판매 수량, 클릭 수” 같은 것들은 사실 테이블로, “제품 이름, 고객 성별, 날짜” 같은 것들은 차원 테이블로 설계하면 됩니다.
스타 스키마
이제 테이블의 구성을 알았으니 차원 모델링을 할 텐데 차원 테이블의 대표적인 구현 방식 중 하나가 바로 스타 스키마입니다
스타 스키마는 이름처럼 사실 테이블 하나에 여러 차원 테이블들이 직접 딱 붙어 있는 별 모양 같은 단순한 구조입니다.
조인이 적어서 쿼리 속도가 빠르고 딱 보면 구조가 이해가 쉽다는 장점이 있습니다.
단점을 굳이 꼽자면 차원 테이블 안에서 정보가 좀 중복될 수는 있습니다. 그래도 워낙 직관적이고 빨라서 가장 널리 쓰이는 방식입니다.
스노우플레이크 스키마
차원 테이블의 대표적인 구현 방식 중 다른 하나는 스노우플레이크 스키마입니다.
스노우플레이크 스키마는 이름 그대로 눈꽃처럼 모양처럼 스타 스키마의 차원 테이블을 더 잘게 쪼개서 정규화한 것입니다.
데이터 중복을 줄일 수 있으니까 저장 공간을 좀 아낄 수 있습니다. 대신에 테이블 조인이 훨씬 많아져서 쿼리가 복잡해지고 성능은 오히려 느려질 수 있습니다.
그래서 스노우플레이크 스키마로 설계를 할 때는 복잡하고 계층적인 차원 구조를 좀 명확하게 관리해야 할 필요가 있을 때 성능 저하 가능성을 염두하고 고려해야합니다.
ETL
분석에 좋은 구조를 만들었다면 이제 실제로 데이터를 그 안으로 가져와야 합니다.
이 과정이 바로 ETL(추출-정제-적재)입니다.
추출
추출은 말 그대로 운영 DB나 로그 파일, 외부 데이터 같은 다양한 소스에서 필요한 데이터를 가져오는 단계입니다. 처음에는 전체 데이터를 다 가져오고(Full Load), 그 다음부터는 바뀐 부분만 골라서 가져오는 방식(Incremental Load)입니다.
변환
변환은 추출해서 가져온 원본 데이터를 깨끗하게 다듬는 작업입니다. 예를들어 결측치 채우고, 날짜 형식 통일하고 여러 소스에서 온 데이터를 통합합니다. 또한 분석 목적에 맞게 데이터를 가공하기도 합니다. 예를들어 고객의 최근 3개월간 구매 금액을 계산하여 새로운 지표를 생성하는 것이 여기에 해당합니다.
적재
적재는 그렇게 잘 변환된 데이터를 데이터 웨어하우스의 미리 만들어 둔 테이블 구조에 맞게 집어넣는 과정입니다. 대량으로 한 번에 넣는 방식(Bulk Load)과, 주기적으로 변경된 것만 업데이트하는 방식(Incremental Load)이 있습니다.
DL - 데이터 레이크
데이터 웨어하우스는 주로 분석을 위해 잘 정제되고 구조화된 데이터(정형 데이터)를 저장합니다. 그리고 데이터, 저장하기 전에 미리 스키마, 즉 테이블 구조를 정의합니다. (Schema on write)
반면에 데이터 레이크는 정형 데이터는 물론이고 이미지, 동영상, 센서 데이터, 로그 파일 같은 반정형, 비정형 데이터까지 모든 형태의 원시 데이터, 로우 데이터를 일단 그대로 다 저장하는 거대한 저장소입니다. 스키마는 데이터를 저장할 때 정하는 게 아니라 나중에 데이터를 읽어서 분석할 시점에 필요에 따라 정의합니다. (Schema on read)
장점은 DW보다 유연하고, 다양한 데이터를 탐색하기 좋습니다. 또한 데이터 레이크는 보통 AWS S3나 Azure Data Lake Storage, Google Cloud Storage 같은 저렴한 객체 스토리지를 사용하기 때문에 많은 양의 데이터도 비용 부담을 좀 덜면서 저장할 수 있습니다.
단점은 관리를 제대로 안 하면 뭐가 뭔지 알 수 없는 데이터스웜프(늪)에 빠질 수 있습니다.
정리하면, 이름에서 알 수 있듯이 데이터 웨어하우스는 데이터를 차곡차곡 쌓아둔 창고와 같고, 데이터 레이크는 모든 자료를 모아둔 거대한 호수 같은 느낌입니다.
데이터 레이크와 데이터 웨어하우스는 어느 하나가 더 좋다고 단정하기보다, 각각의 용도에 따라 다르게 활용된다고 보면 됩니다. 그래서 이 둘의 장점만 합친 데이터 레이크 하우스라는 아키텍처도 있습니다.
빅데이터
데이터의 양이 너무 방대하거나(Volume), 생성 속도가 너무 빠르거나(Velocity), 형태가 너무 다양하여(Variety) 기존의 관계형 데이터베이스 관리 시스템(RDBMS)으로는 처리하기 어려운 데이터를 빅데이터라고 합니다. 이 세 가지 특성을 3V라고 하며, 여기에 데이터의 신뢰성(Veracity)과 가치(Value)를 더해 5V라기도 하고, 정확성(Validity)와 휘발성(volatility)을 더해 7V라기도 합니다.
이러한 빅데이터를 처리하기 위해서는 하둡이나 스파크같은 분산 처리 시스템이 필요합니다. 단일 서버의 한계를 뛰어넘어, 수많은 서버(노드)에 데이터를 분산 저장하고 병렬로 처리함으로써 대규모 데이터 작업을 빠르고 안정적으로 수행할 수 있게 됩니다.
Hadoop 에코시스템
Hadoop은 대규모 데이터를 분산 저장하고 처리하기 위한 오픈 소스 프레임워크입니다.
Hadoop은 다음과 같이 세 가지로 구성됩니다.
- HDFS: 데이터를 잘게 쪼개서 분산 저장하는 파일 시스템
- MapReduce: 분산된 데이터를 병렬로 처리하는 프로그래밍 모델
- YARN: 클러스터의 자원을 관리하고 다양한 애플리케이션을 실행하는 프레임워크
HDFS
HDFS는 하둡의 모듈로서 데이터를 잘게 쪼개서 분산 저장하는 파일 시스템입니다.
데이터를 저장하는 방식은 다음과 같습니다.
- 데이터를 128M 블록 단위로 잘게 쪼갭니다.
- 그걸 여러 컴퓨터(데이터 노드) 서버들에 분산시켜서 저장을 합니다.
- 혹시 모르니까 각 블록을 기본적으로 세 군데 복제를 해둡니다.
장점은 복제를 해두니깐 서버 하나가 문제가 생겨도 데이터는 안 날아가는 장점이 있습니다.
그리고 네임노드라는 게 있는데, 지도라고 보면 됩니다. 어떤 데이터 블록들이 어디 저장되어 있는지 정보를 관리하는 역할을 합니다. 즉, 메타데이터를 관리합니다. 또한 데이터에 대한 사용자 접근 권한도 관리하기도 합니다.
⤷ HDFS 아키텍처
위에 HDFS 아키텍처에서 빨간색을 보면 Client - Read (읽기)과정이 있는데요. 설명하자면 클라이언트가 파일을 읽으려고 할때 NameNode에 파일의 블록 위치 정보를 요청하면 NameNode는 클라이언트에게 해당 블록을 저장하고 있는 DataNode의 정보를 알려줍니다. 그럼 클라이언트는 제공받은 DataNode 목록 중에서 네트워크적으로 가장 가까운 DataNode를 선택하여 해당 DataNode에게 특정 블록을 요청합니다. 선택된 DataNode는 요청받은 블록을 자신의 로컬 파일 시스템에서 찾아 클라이언트에게 직접 스트림 방식으로 데이터를 전송합니다.
Client - Write (쓰기)는 클라이언트는 NameNode로부터 할당받은 DataNode에 직접 데이터 블록을 스트림 방식으로 전송합니다. 이때 하나의 DataNode에 데이터를 저장 하고 끝나는게 아니라, 동시에 다른 두 번째, 세 번째 DataNode에 데이터를 전달하여 복제(Replication)를 진행합니다.
MapReduce
HDFS가 데이터를 효율적으로 저장하는 시스템이라는것을 알았으니 이제는 그 저장된 데이터를 효율적으로 “처리”하는 MapReduce에 대해 알아봅니다.
MapReduce는 HDFS에 저장된 대규모 데이터를 병렬로 처리하기 위한 프로그래밍 모델입니다.
먼저 쉽게 설명하자면, MapReduce는 “많은 사람들에게 작업을 나눠주고, 그 결과들을 모아서 최종 결과물을 만드는 효율적인 작업 지시 및 취합 방식” 이라고 할 수 있습니다.
MapReduce는 이름에서 알 수 있듯이, Map과 Reduce라는 단계와 중간에 Shuffle & Sort 단계로 구성됩니다.
⤷ MapReduce
-
Map 단계:
입력 데이터를 작은 조각들로 분할하여 각각의 Mapper(매퍼) 작업에 할당합니다. 각 Mapper는 할당된 데이터 조각을 처리하여 (key, value)형태로 변환합니다. -
Shuffle & Sort 단계:
Map 단계에서 생성된 중간 (key, value) 쌍들을 Reduce 단계로 보내기 전에, 동일한 key를 가진 value들을 한 곳으로 모아서 정렬합니다. -
Reduce 단계:
Shuffle & Sort 단계를 통해 전달받은 value 리스트를 집계, 요약해서 최종 (key, value) 쌍의 결과물을 생성합니다.
하지만 이 맵리듀스가 처리 중간 결과들을 계속 디스크에다 썼다 지웠다해서 속도가 좀 느립니다. 그래서 Spark를 사용하게 되는데 Spark는 마지막에 설명하겠습니다.
YARN
아까 알아보았던 MapReduce의 처리 작업이 실제로 어떤 노드에서, 얼마만큼의 자원을 가지고, 언제 실행할지를 결정하고 관리하는 시스템이 필요합니다. 이때 MapReduce가 클러스터 자원을 효율적으로 사용할 수 있도록 “지휘”하는 역할이 YARN입니다.
즉, YARN은 데이터 처리 작업을 위한 자원 관리와 작업 스케줄링 프레임워크입니다.
⤷ YARN 아키텍처
YARN은 세 가지 주요 컴포넌트로 구성됩니다.
-
ResourceManager (리소스매니저): 클러스터 전체의 모든 자원(CPU, 메모리)을 파악하고, 여러 애플리케이션(작업)들이 요청하는 자원을 공정하고 효율적으로 배분하는 역할을 합니다.
-
NodeManager (노드매니저): 자신이 맡은 노드 안에서 ResourceManager의 지시를 받아 실제 자원(Container)을 만들고, 그 안에서 작업을 실행하며, 노드의 자원 사용량을 ResourceManager에게 보고하는 역할을 합니다.
-
ApplicationMaster (애플리케이션 마스터): 하나의 MapReduce 혹은 Spark를 YARN에서 실행되는 각 애플리케이션마다 별도로 생성됩니다. 해당 애플리케이션의 자원 요청부터 실제 태스크 실행, 그리고 작업 진행 상황 모니터링까지 전반적인 책임을 맡습니다.
Spark
이제는 아까 전에 언급했던 MapReduce의 느린 속도를 보완하는 도구인 Spark에 대해 알아봅니다.
Spark는 대규모 데이터 처리 및 분석을 위한 빠른 클러스터 컴퓨팅 프레임워크입니다. MapReduce가 중간 결과를 디스크에 저장하여 속도 제약이 있던 것과 달리, Spark는 데이터를 메모리에 올려놓는 인메모리 방식으로 처리합니다. 덕분에 반복적인 작업에서 MapReduce보다 빠른 성능을 자랑합니다.
⤷ MapReduce processing vs Spark processing
또한 MapReduce는 배치 처리만을 하지만, Spark는 배치 처리를 포함하여 스트리밍, 대화형 쿼리, 머신러닝, 그래프 처리 기능을 가지고 있습니다.
⤷ Spark 코어
Spark Core는 Spark 플랫폼의 기초입니다. 메모리 관리, 장애 복구, 스케줄링, 작업 배포 및 모니터링, 스토리지 시스템과의 상호 작용을 담당합니다. Spark Core는 RDD라는 추상화를 통해 이런 기능들을 제공하고, Java, Scala, Python, R용으로 구축된 API를 통해 개발자들이 Spark의 분산 처리 기능을 손쉽게 활용할 수 있도록 합니다.
※ RDD는 스파크의 기본 데이터 구조입니다. 분산 변경 불가능한 객체 모음이며 스파크의 모든 작업은 새로운 RDD를 만들거나 존재하는 RDD를 변형하거나 결과 계산을 위해 RDD에서 연산하는 것을 표현하고 있습니다.
Spark 아케텍처
⤷ Spark 아키텍처
- Driver Program (드라이버 프로그램): Spark 애플리케이션의 두뇌 역할을 합니다. 작성한 Spark 코드가 실행되는 곳이며, 전체 작업의 흐름을 지시하고 태스크를 분할하며, 클러스터에 자원을 요청하고 결과를 수집합니다.
- Cluster Manager (클러스터 매니저): Spark 애플리케이션에게 클러스터의 자원(CPU, 메모리)을 할당해주는 자원 배분자입니다. YARN, Apache Mesos, Kubernetes, Spark 자체의 Standalone(독립형) 모드가 있습니다.
- Executors (익스큐터): 각 워커 노드(실제 데이터가 있는 서버들)에서 실행되는 실제 일꾼들입니다. 드라이버가 지시하는 계산 작업을 수행하고, 중간 결과를 저장하거나 데이터를 읽고 쓰는 역할을 합니다.
이미지 출처:
· Tracking Garden
· NashTech Blog
· Nix United
· DataBricks
· TechVidvan
· Apache
· moons08
· AWS
· EduinPro
Leave a comment