다양한 미디어 생태계에서 디지털 광고가 전체 광고 매출에서 차지하는 비중이 커지고 있습니다. 이로 인해, 광고 클릭 이벤트에 대한 추적 작업도 매우 중요해지고 있죠. 이번 장은 이런 추적 작업을 위한 광고 클릭 이벤트를 집계하는 기능을 개발하는 것에 대해서 설명하고 있습니다.
광고 클릭 이벤트 집계는 아래와 같은 영향을 끼칩니다.
- 온라인 광고가 얼마나 효율적이었는지 측정함으로써, 광고주가 얼마나 많은 돈을 지불할지 결정
- 광고 예산 조정 및 타겟 그룹이나 키워드 변경하는 광고 전략 수정
따라서, 광고 클릭 이벤트 집계 결과 데이터의 정확성은 매우 중요합니다.
기능 요구사항
- 이전 M분 동안의 ad_id 클릭 수 집계
- 매분 가장 많이 클릭된 상위 N개 ad_id 반환
- 다양한 속성에 따른 집계 질의 결과 필터링 지원
- 데이터의 양
- DAU (Daily Active User) : 10억 명
- 한 사용자의 하루 평균 광고 클릭 이벤트 수: 1건
- 하루에 10억 건의 광고 클릭 이벤트 발생
- 광고 클릭 QPS: 10억/(100,000초) = 약 10,000 (책에서 하루를 100,000초로 계산)
- 최대 QPS는 평균의 5배라 가정 = 약 50,000
- 광고 클릭 이벤트 하나당 0.1KB 저장 용량 필요 → 일일 저장량: 0.1KB x 10억 = 100GB, 월간 저장량: 약 3TB
- DAU (Daily Active User) : 10억 명
비기능 요구사항
- 집계 결과 정확성
- 지연된 혹은 중복된 이벤트에 대한 적절한 처리 기능
- 견고성: 부분적인 장애 감내 가능
- 지연 시간 요구사항: 전체 처리 시간은 최대 수 분을 넘지 않아야 함
계략적 설계안
질의 API 설계
데이터 모델
책에서는 원시 데이터와 집계 결과 데이터, 이렇게 두 가지 데이터 종류를 다루고 있습니다.
원시 데이터는 말 그대로 원본 데이터로, 로그로부터 얻어낸 데이터입니다. 집계 결과 데이터는, 원시 데이터로부터 매분 집계하고 필터링을 적용한 데이터라고 할 수 있습니다.
[원시 데이터 예시]
ad_id | click_timestamp | user_id | ip | country |
ad001 | 2021-01-01 00:00:01 | user1 | 207.148.22.22 | USA |
[집계 결과 데이터 예시]
ad_id | click_minute | filter_id | count |
ad001 | 202101010000 | 0012 | 2 |
두 데이터를 모두 저장하는 것을 추천하고 있습니다. 문제가 발생하면, 집계 결과 데이터로만으로는 원인을 추적하기 어렵고, 반대로 원시 데이터만 존재하는 경우, 원시 데이터의 양이 엄청나므로 직접 질의하는 것이 비효율적 이기 때문입니다.
데이터베이스 선택
올바른 데이터베이스를 선택하려면 아래 사항들을 평가해 보아야 합니다.
- 데이터는 어떤 모습인지? 관계형 데이터? 문서 데이터? BLOB?
- 작업 흐름이 쓰기 중심인지? 읽기 중심인지?
- 트랜잭션을 지원해야 하는지?
- 질의 과정에서 OLAP(온라인 분석 처리 - SUM, COUNT 등) 함수를 많이 사용해야 하는지?
원시 데이터의 경우, 광고 클릭 집계 이벤트의 최대 QPS를 50,000으로 추정했으므로 쓰기 중심 시스템에 사용될 것입니다. 원시 데이터는 백업 역할, 그리고 재계산이 필요할 경우 재계산 용도로만 이용되므로 이론적으로는 읽기 연산 빈도가 낮습니다.
집계 데이터의 경우, 읽기 연산과 쓰기 연산 둘 다 많이 사용할 것입니다. 매 분마다 데이터베이스에 질의를 던져 최신 집계 결과를 제공해야 하고(읽기), 그 결과를 기록(쓰기) 하기 때문이죠.
이 책에서는 두 데이터에 대해 같은 데이터베이스로 cassandra를 선택하였습니다.
비동기 처리
이벤트 발행(생산자)과 처리 시스템(소비자)의 결합을 카프카와 같은 메시지 큐로 끊음으로써 데이터를 비동기식으로 처리하면, 트래픽이 갑자기 증가하여도 안정적으로 시스템을 운영할 수 있습니다.
설계안
집계 서비스
MapReduce를 사용하여 광고 클릭 이벤트를 집계합니다. Map 노드는 원시 데이터에서 읽은 데이터를 필터링하고 변환하는 역할을 담당합니다. 예를 들어, Map 노드는 ad_id % 2 = 0의 조건을 만족하는 데이터를 노드 1로 보내고, 그렇지 않은 데이터는 노드 2로 보내는 역할을 할 수 있습니다. Reduce 노드는 '집계' 노드에서 Map 노드로부터 얻은 데이터를 매 분 메모리에서 집계할 때 사용됩니다. 그 후, '집계' 노드가 산출한 결과를 Reduce 노드로 최종 결과를 축약합니다.
상세 설계
데이터 재계산
집계 서비스에 중대한 버그가 있어서 원시 데이터를 다시 읽어 집계 데이터를 재계산해야 하는 경우가 있을 것입니다. 이를 위해 재계산 서비스는 원시 데이터 저장소에서 데이터를 검색하고, 재계산 전용 데이터 집계 서비스에서 처리하도록 합니다.
집계 결과 전달 보장
카프카와 같은 메시지 큐는 at-most once, at-least once, exactly once 방식을 지원합니다. 광고 집계 시스템은 중복된 데이터가 있으면 안 되기 때문에, exactly once로 정확하게 한 번만 처리하도록 해야 합니다. 이를 위해 분산 트랜잭션 아키텍처를 사용할 수 있습니다.
- 분산 트랜잭션 아키텍처: 여러 시스템, 서버 또는 데이터베이스에 걸쳐 분산된 트랜잭션을 처리하는 방식입니다. 즉, 하나의 트랜잭션이 여러 네트워크에 분산된 자원에 대해 동시성, 일관성, 고립성, 내구성(ACID 특성)을 보장하는 구조입니다.
시스템 규모 확장
- 메시지 큐 규모 확장: 소비자를 추가하여 재조정 작업을 진행하고, 사전에 충분한 파티션을 마련하며, 데이터를 여러 토픽으로 나눔으로써 시스템의 처리 대역폭을 높일 수 있습니다.
- 집계 서비스 규모 확장: MapReduce 노드의 추가/삭제를 통해 수평적으로 규모를 조정할 수 있습니다. 그리고, ad_id마다 별도 처리 스레드를 두어 처리량을 높일 수 있습니다.
- 데이터베이스 규모 확장: Cassandra는 기본적으로 수평적인 규모 확장을 지원하고 있습니다.
핫스팟 문제
특정 광고에 더 많은 클릭이 발생함으로써 특정 ad_id에 요청이 몰리는 경우 서버 과부하 문제가 발생할 수 있는데, 이를 핫스팟 문제라고 합니다. 이 문제는 해당 ad_id 처리에 더 많은 집계 서비스 노드를 할당하여 완화할 수 있습니다.
결함 내성 (fault tolerance)
'시스템 상태'(upstream offset, 지난 M분간 가장 많이 클릭된 광고 N개 같은 데이터 등) 에 해당하는 정보를 Snapshot으로 저장하고, 마지막 저장된 상태부터 데이터를 복구해 나가는 방식으로 처리할 수 있습니다.
지속적 모니터링
Latency가 데이터를 처리하는 단계마다 발생할 수 있는데, timestamp를 추적 가능하도록 기록된 시간 차이를 latency 지표로 변환해 모니터링할 수 있습니다.
또한, 메시지 큐의 크기가 갑자기 늘어난다면, 더 많은 집계 서비스 노드가 필요하게 될 텐데 이를 추적하기 위해서 records-lag와 같은 지표를 추적할 수 있습니다.
CPU, 디스크, JVM 과 같은 집계 노드의 시스템 자원도 모니터링 해야 합니다.
데이터 정확성
Reconciliation (조정)은 다양한 데이터를 비교하여 데이터 무결성을 보증하는 기법입니다. 광고 클릭 집계 결과의 무결성을 보증하는 한 가지 방법은 실시간 클릭 이벤트 데이터(원시 데이터)를 batch 작업으로 매일 이벤트 발생 시각에 따라 정렬한 결과를 만들어서, 실시간 집계 결과 (집계 데이터)와 비교해 보는 것입니다.
최종 설계안
'Study' 카테고리의 다른 글
대규모 시스템 설계 기초 2 - 5장. 지표 모니터링 및 경보 시스템 (3) | 2024.11.16 |
---|---|
대규모 시스템 설계 기초 2 - 4장. 분산 메시지 큐 (3) | 2024.11.07 |
대규모 시스템 설계 기초 2 - 3장. 구글맵 (0) | 2024.11.02 |