1단계: 원천 시스템에서의 데이터 생성
이 글은 “견고한 데이터 엔니지어링”의 5장 “1단계: 원천 시스템에서의 데이터 생성” 챕터를 읽고 쓰는 글입니다.
원천 시스템의 주요 아이디어
1. 파일과 비정형 데이터
파일은 바이트의 시퀀스로 디스크에 저장되며 로컬 매개변수, 이벤트, 로그, 이미지 및 오디오를 저장 할 수 있습니다.
정형 데이터
- 엑셀
- CSV
반정형 데이터
- JSON
- XML
- CSV
비정형 데이터
- TXT
- CSV
압축 및 컬럼 기반 파일 등
- Parquet
- Avro
- ORC
등 다양한 형태가 있습니다. 물론 이 외에도 png, jpeg, mp4 등 다양한 이미지/동영상 파일이 포함되어 있습니다.
2. API
요즘 API를 통한 데이터 수집이 활성화되고 있습니다. Fluentd를 사용하던때도 POST Request에서 Request Body에 담긴 JSON을 읽어서 수집하는 방법도 고려되었는데, 이러한 API로 원천 데이터가 생성되는 사례가 많아지고 있습니다.
3. OLTP & OLAP
3.1 ) OLTP
온라인 트랜잭션 처리(OLTP)는 일반적으로 많은 수의 사용자가 애플리케이션과 동시에 상호 작용하고 데이터를 갱신하거나 작성 할 때 애플리케이션 백엔드로 자주 사용되는 방식입니다. 하지만 방대한 양의 데이터를 분석하는 용도를 적합하지 않습니다.
대표적으로 OLTP 개념을 활용할 때 RDB를 사용합니다. 특정 행을 탐색하고 데이터를 조회하는 데이터베이스이기 때문에 데이터를 찾는 것에 장점이 있지만, 방대한 양의 레코드(행)을 메모리에 올리면 불필요한 부하가 생긴다는 점과 실제 디스크내에 저장 된 데이터가 연속적이지 않게 위치하면서 원하는 데이터를 효과적으로 찾기 위해 인덱스가 (거의) 필수적이라는 점에서 대용량의 데이터를 다루기에는 적합하지 않을 수 있습니다.
3.2 ) OLAP
온라인 분석 처리(OLAP)는 반대로 방대한 양의 데이터를 분석하는 용도로 자주 사용됩니다. 주로 컬럼기반의 데이터베이스를 활용하기 때문에 실제 디스크에 거의 연속적으로 저장되어 있는 열 단위의 데이터를 읽어 필요한 열만 메모리에 올릴 수 있다는 점과 그로인해 디스크 IO의 높은 효율을 가진다는 점으로 대용량 처리에 적합하게 사용됩니다.
OLTP의 트랜잭션에 대한 포스트와 OLTP/OLAP의 주요한 특징에 대해서 포스트로 남긴 적이 있기 때문에 포스트 링크로 대신하겠습니다.
4. 변경 데이터 캡처 ( CDC )
이 내용도 간략하게 적어놓은 포스트가 있어서 대체하겠습니다.
5. 로그
실제 Nginx 웹 서버의 access.log를 활용하여 로그를 수집 한 경험을 바탕으로 쓴 포스트가 있습니다. 하지만 로그 부분에 대해서는 더 자세하게 적을 필요성이 있을 것이라 생각되어 내용을 추가하도록 하겠습니다.
로그는 풍부한 데이터 원천으로 다운스트림 데이터 분석, ML 및 자동화에 유용합니다. 일반적으로 다음과 같은 형태를 띄고 있습니다.
- 운영 체제 ( CPU, Memory 등의 자원 )
- 애플리케이션
- 서버 ( DEBUG, INFO, ERROR 등 )
- 컨테이너
- 네트워크
- IOT 장치
모든 로그는 이벤트와 이벤트 메타데이터를 추적한다고 합니다. 최소한 누가, 무엇을, 언제 수행했는지를 수집해야 합니다.
- 누구인가
- 이벤트와 관련이 있는 사람
- 시스템 또는 서비스 계정
- 무슨일이 있었는가
- 이벤트 및 관련 메타데이터
- 언제 발생했는가
- 이벤트의 타임스탬프
5.1 로그 인코딩
로그는 몇 가지 방법으로 인코딩됩니다.
- 바이너리 인코딩 로그
- 공간 효율과 빠른 IO를 위해 데이터를 사용자 정의 압축 형식으로 인코딩합니다. 데이터베이스 로그가 일반적입니다.
- 반정형 로그
- 객체 직렬화 형식(JSON과 같은)의 텍스트로 인코딩합니다. 이러한 형태는 이식성이 뛰어나지만, 바이너리 로그보다 효율성이 훨씬 떨어집니다.
- 주로 직렬화하는 별도의 파싱 코드를 작성해서 사용하기도 합니다.
- 일반 텍스트(비정형) 로그
- 따로 정해진 형식이 없이 사용자에 설정에 따라 저장되는 로그입니다.
- 유용한 정보를 얻기 위해서는 커스텀 코드가 필요하지만, 자유롭게 데이터를 수집 할 수 있고 분석가/ML 엔지니어에게 유용한 정보를 제공 할 수 있습니다.
5.2 로그 해상도
로그 해상도는 로그에 캡처된 이벤트 데이터의 양을 말합니다. 로그에 포함된 데이터의 양으로 간략화해서 말 할 수 있을 것 같습니다. CDC의 경우에는 데이터베이스의 Row단위 변경 사항이 될 것 같고, Fluentd에서 POST를 통해 데이터를 수집한다면 포함된 Request Body가 이 로그 해상도라고 볼 수 있을 것 같습니다.
하지만 일반적으로 서버의 로그를 얻을 때는 우리에게 익숙한 로그 레벨을 사용해서 특정 데이터만 처리하기도 합니다. 여기서 로그 레벨은 DEBUG, INFO, ERROR와 같은 로그의 특성을 말합니다.
5.3 데이터베이스 로그
위에서 소개한 CDC, 혹은 데이터베이스에서 자체적으로 남기는 redolog 혹은 binlog 등을 잘 공부하면 될 것 같습니다.
5.4 메시지와 스트림
Kafka와 RabbitMQ와 같은 메시지 큐(브로커)를 사용하면 메시지를 실시간으로 전달받아서 사용 할 수 있게 됩니다. 이러한 내용은 RabbitMQ 사용기 포스트와 Kafka에 대한 포스트를 보시면 간략하게 알 수 있습니다.
하지만 여기서 스트림이라는 개념은 조금 더 세부적으로 알아 볼 필요는 있습니다. 기본적으로 분산 서비스에서는 모든 이벤트가 순서대로 들어온다는 것을 보장하기 어렵습니다. 그렇기 때문에 이벤트 발생 당시에 찍힌 타임스탬프를 활용하여 이벤트를 정렬 후 사용해야 합니다. 자세한 내용은 분산 시스템에서의 일관성과 합의 포스트에서 다루기도 했으며, 분산 시스템의 골칫거리에서는 시계를 믿을 수 없음도 알았습니다.
스트림은 이벤트 레코드의 추가 전용 로그로써 이벤트가 발생하면 순서대로 누적됩니다. 하지만 순서대로 이벤트가 입력되지 않기 때문에 순서가 필요하다면 정렬하는 단계가 필요합니다.
이전의 포스트에서도 이벤트 자체의 고유 ID에 순차를 적용하여 정렬하는 방식으로 문제를 해결 할 수 있으며, 타임스탬프 자체의 정렬로도 문제를 해결하는 등의 다양한 고민이 들어있습니다.
5.5 시간 유형
타임스탬프는 데이터 엔지니어에게 매우 중요한 특징입니다.
저는 이전 회사에서 대부분의 업무는 분석을 위한 배치 파이프라인를 작성하는 업무였습니다. 이 경우에는 사실 타임스탬프의 기준은 시간단위이기 때문에 큰 어려움은 없었습니다. 몇 초 단위의 이벤트의 경우에는 오차가 발생하더라도 전체 모수에 큰 영향을 끼치지 않았으니까요.
하지만 스트리밍에 가까운 분석 및 ML 서비스 서빙으로 들어가면 문제는 완전히 달라집니다. 추천 모델에 이전 피처보다 먼저 이벤트가 입력되면 가장 큰 영향을 줄 수 있는 이전 이벤트를 배제 한 채로 추천 아이템이 노출 될 수 있고, 그 외에도 SRE 입장에서는 에러 시점이 엇갈릴 수 있습니다. 로그로 문제를 찾아야 하는 담당자 입장에서는 어려울 수 있죠.
그래서 타임스탬프를 잘 활용해야 하며, 타임스탬프를 찍는 시점은 몇 개가 있습니다.
- 이벤트 시간
- 원본 이벤트 자체의 타임스탬프를 포함하여 원천 시스템에서 이벤트가 생성 된 시점입니다.
- 수집 시간
- 원천 시스템에서 메시지 대기열, 캐시, 메모리, 객체 스토리지, DB 또는 디스크등에 이벤트가 저장 된 시점입니다.
- 처리 시간
- 수집 이후에 데이터가 실질적으로 처리 된 시간 ( 과 처리에 걸린 시간 )입니다.
그래프 데이터베이스
이건 이 책을 읽고 진행하는 스터디에서 알게 된 내용으로 추후에 사용 할 수 있는 기회가 있을 것 같아 적어 놓습니다.
그래프 데이터베이스는 두 노드와 두 노드 사이의 관계를 포함하여 저장하는 데이터베이스입니다. 이 특징은 데이터 사이의 방향성을 만들어 낼 수 있음을 의미하며, 무려 벡터화를 편하게 할 수 있도록 도움을 준다는 점을 알게 되었습니다.
추천 시스템에 혹시 기여 할 일이 있다면, 프로토타입으로 한 번 만들어보고 싶은 생각이 듭니다.
GraphQL
사실 길게 이야기 할 내용은 없지만, 상당히 독특한 내용이라서 적어봅니다.
일단 함께 스터디를 진행해주신 데엔님께서는 “요즘 프론트엔드가 GraphQL을 자주 사용하더라”라고 말씀해주셨습니다. 알아보니 복잡한 SQL문을 작성하는 것 대신 JSON에 가까운 문자열로 원하는 데이터를 쉽게 얻어내는 방법임을 알게 되었습니다. 조인등이 저절로 옵티마이징 된다고 하는데, 효율성으로는 아직 부족한 점이 많지만 난이도가 매우 쉽기 때문에 자주 활용되는 것 같습니다.