////
Search
🥍

6장. 광고 클릭 이벤트 집계

인터넷 산업이 발전하며 디지털 광고가 전체 광고 매출에서 차지하는 비중이 날로 커지고 있다. 그 결과 광고 클릭 이벤트 추적 작업의 중요성도 지극히 높아지고 있는 실정이다.
이번 장에서는 페이스북이나 구글 규모에 걸맞는 광고 클릭 이벤트 집계 시스템을 설계해 본다.

1단계: 문제 이해 및 설계 범위 확정

기능 요구사항

지난 M분 동안의 ad_id 클릭 수 집계
매분 가장 많이 클릭된 100개 광고 아이디를 반환
다양한 속성에 따른 집계 필터링을 지원
데이터의 양은 페이스북이나 구글 규모

비기능 요구사항

집계 결과 정확성은 데이터가 RTB 및 광고 과금에 이용되므로 중요
지연되거나 중복된 이벤트를 적절히 처리할 수 있어야 함
부분적인 장애는 감내할 수 있는 견고성이 필요함
전체 처리 시간은 최대 수 분을 넘지 않아야 함

개략적 추정

일간 능동 사용자 수는 10억 명
각 사용자는 하루에 평균 1개 광고를 클릭한다고 가정하므로 대략 10억 번의 광고 클릭 이벤트가 발생
광고 클릭 QPS 10억 / 86400 = 대략 10,000
최대 광고 클릭 QPS는 평균 QPS의 다섯배로 가정
광고 클릭 이베늩 하나 당 0.1KB의 용량이 필요하다고 가정하면 일일 저장소 요구량은 대략 100GB, 월간은 대략 3TB

2단계: 개략적 설계안 제시 및 동의 구하기

질의 API 설계

지난 M분 동안 각 ad_id에 발생한 클릭 수 집계
지난 M분 동안 가장 많은 클릭이 발생한 상위 N개 ad_id 목록 반환
다양한 속성을 기준으로 집계 결과를 필터링하는 기능 지원

데이터 모델

원시 데이터, 집계 결과 데이터 둘로 저장해야 한다.
원시 데이터의 경우, 디버깅에 사용하기 적합하며 집계 데이터에 오류가 있을 경우, 수정하여 집계 결과를 다시 만들 수도 있다.
원시 데이터는 백업 데이터로 활용되며 그 용량자체가 거대하므로 질의에 적합하지 않아 집계 결과 데이터 조회를 통한 질의로 성능 이슈를 방지할 수 있다.

올바른 데이터베이스의 선택

평균 쓰기 QPS 10,000과 최대 쓰기 QPS 50,000 등의 지표들을 토대로 적절한 데이터베이스를 선택해야 한다.
일단 이 데이터베이스 시스템은 쓰기 연산에 집중되어 있다. 따라서 관계형 데이터베이스도 구현은 가능하나 가급적 쓰기 및 시간 범위 질의에 최적화된 카산드라나 InfluxDB를 사용하는 것이 좀 더 바람직하다.

개략적 설계안

비동기 처리
카프카 같은 메시지 큐를 도입하여 생산자와 소비자 간의 결합을 끊을 수 있다.
급격한 클릭 수의 증가 등으로 인해 소비자의 처리 용량이 초과되는 경우, 이슈가 발생할 수 있는데 이를 방지할 수 있다.

집계 서비스

광고 클릭 이벤트를 집계하는 좋은 아이디어 중 하나는 맵리듀스 프레임워크를 사용하는 것이다.
유향 비순환 그래프가 해당 프레임워크에 적합한 모델인데, 이 모델의 핵심은 시스템을 맵/집계/리듀스 노드 등의 작은 단위로 세분화하는 것이다.
각 노드는 한 작업만 처리하며 처리 결과를 다음 노드에 인계한다.
맵 노드
데이터 출처에서 읽은 데이터를 필터링하고 변환하는 작업을 담당한다.
집계 노드
ad_id 별 광고 클릭 이벤트 수를 매 분 메모리에서 집계한다.
리듀스 노드
모든 집계 노드가 산출한 결과를 최종 결과로 축약한다.

3단계: 상세 설계

스트리밍 vs 일괄 처리

해당 시스템은 일괄 및 스트리밍 처리를 동시에 지원하며 이런 아키텍처를 람다라고 부른다.
스트림 처리는 데이터를 오는 대로 처리하고 거의 실시간으로 집계된 결과를 생성하는데 사용하며 일괄 처리는 이력 데이터를 백업하는 데 사용한다.

시간

해당 데이터에서 시점은 두 가지가 존재할 수 있다. 이벤트 시각과 처리 시각인데, 이 둘은 네트워크 지연이나 비동기적 처리 환경에 따라 차이가 발생할 수 있다.
어떤 데이터를 사용하느냐에 따라 장단점이 달라진다. 두 방안의 장단점을 고려하여 보다 적절한 설계를 만드는 것이 적합하다.
장점
단점
이벤트 발생 시각
광고 클릭 시점을 정확히 아는 것은 클라이언트이므로 집계 결과가 보다 정확해진다.
클라이언트가 생성한 타임스탬프에 의존하는 방식이므로 클라이언트의 시각에 문제가 있거나 조작된 경우, 문제가 발생할 수 있다.
처리 시각
서버의 타임스탬프가 클라이언트보다 안정적이다.
이벤트가 시스템에 늦게 도착하는 경우 집계 결과가 부정확해진다.
앞서 기능 요구사항 가정에서 정확도가 더 중요하다 했으므로 클라이언트 시점의 타임스탬프를 사용한다는 가정을 해볼 수 있다.

집계 윈도

윈도에는 텀블링 윈도(고정 윈도), 호핑 윈도, 슬라이딩 윈도, 세션 윈도의 네 종류가 있다. 이 중, 본 설계안과 유관한 텀블링, 슬라이딩을 알아보자.
텀블링 윈도는 시간을 같은 크기의 겹치지 않는 구간으로 분할한다. 따라서 매 분 발생하는 클릭 이벤트를 집계하기에 아주 적합하다.
슬라이딩 윈도는 데이터 스트림을 미끄러져 나아가면서 일정한 시간 구간 안에 있는 이벤트를 집계한다. 슬라이딩 윈도우는 서로 겹칠 수 있다.
이는 앞선 두 번째 요구사항인 지난 M 분간 가장 많이 클릭한 상위 N개 광고 수 기능을 구현하기에 적합하다.

전달 보장

집계 결과는 과금 등에 활용되므로 데이터의 정확성, 무결성이 아주 중요하다.
때문에 시스템은 다음 두 질문에 대답할 수 있어야 한다.
이벤트의 중복 처리를 어떻게 피할 것인가?
카프카와 같은 메시지 큐를 사용해 처리할 수 있다. 이때 정확히 한 번의 전달 방식을 사용한다면 좋을 것이다.
모든 이벤트의 처리를 어떻게 보장할 것인가?
중복 데이터는 다양한 지점에서 발생할 수 있는 이번 절에서는 흔한 두 사례만을 살펴보자.
클라이언트 측
한 클라이언트가 같은 이벤트를 여러번 보내는 경우다. 이런 중복 이벤트를 처리하는 데는 광고 사기/위험 제어 컴포넌트 등의 계층을 하나 더 두어 처리하는 것이 적합하다.
서버 장애 측
집계 도중에 집계 서비스 노드에서 장애가 발생하여 업스트림 서비스가 이벤트 메시지에 응답하지 못했다면 재전송 로직을 통해 중복 집계될 가능성이 있다.
이 문제의 가장 간단한 해결책은 HDFS나 S3와 같은 외부 저장소에 이벤트의 오프셋을 기록하여 처리하는 것이다.
그러나 이 방법도 문제가 있는데, 저장소에 저장된 오프셋 이전의 이벤트는 전부 무시한다는 것이다. 오프셋 갱신 직후 집계 서비스 노드에 문제가 생긴다면 데이터가 유실될 가능성이 있다.
따라서 데이터 손실을 막기 위해 다운스트림에서 집계 결과 수신 확인 응답을 받은 후, 오프셋을 저장해야 한다.

시스템 규모 확장

메시지 큐의 규모 확장
생산자와 소비자는 모두 어렵지 않게 확장을 달성할 수 있다. 생산자는 그저 인스턴스를 늘리면 그만이며 소비자의 경우 재조정 메커니즘을 통해 달성할 수 있다.
브로커의 경우, 해시 키와 파티션의 수, 토픽의 샤딩 등에 대한 고려가 필요하다.
해시 키의 경우, 가급적 같은 이벤트는 같은 파티션에서 처리하도록 식별자를 선택하는 것이 좋다.
파티션의 수의 경우, 사전에 충분한 수를 확보하여 프로덕션 환경에서 파티션의 수가 동적으로 변경되는 것을 방지하는 것이 좋다.
그렇지 않은 경우, 해시 키에 따른 파티션 할당이 변경되어 같은 식별자를 가졌음에도 규모 확장 후 다른 곳에 저장되거나 하는 등의 불상사가 발생할 수 있다.
토픽의 물리적 샤딩의 경우, 지역이나 비즈니스 유형 등 분류에 다라 여러 토픽으로 나누어 시스템의 처리 대역폭을 향상시킬 수 있다. 그러나 복잡성이 증가하고 유지 비용이 증가한다.
집계 서비스의 규모 확장
앞서 이 시스템은 맵리듀스 연산을 사용하는 데 이 경우, 각 노드들이 상호 연결되어 연산을 처리한다.
이 서비스의 규모 확장을 달성하기 위해서는 ad_id마다 별도의 처리 스레드를 두거나 집계 서비스 노드를 아파치 하둡 YARN 같은 자원 공급자에 배포하여 다중 프로세싱을 활용할 수 있다.
데이터베이스의 규모 확장
카산드라는 안정 해시와 유사한 방식으로 수평적 규모 확장을 기본적으로 지원하고 있다. 따라서 수동적으로 샤딩을 조정하는 과정은 필요하지 않다.
하지만 어느정도 알고 있는 블랙박스로 두는 것이 좋으므로 어느정도 실행 과정 정도는 알아두는 것이 좋다.

핫스팟 문제

다른 서비스나 샤드보다 더 많은 데이터를 수신하거나 처리하는 샤드를 핫스팟이라 부른다.
이 경우, ad_id에 따라 발생할 수 있는데, 큰 회사는 더 많은 광고를 통해 클릭을 유도하기 때문에 핫스팟 문제가 발생할 수 있다.
이 문제는 더 많은 집계 서비스 노드를 할당하여 해결할 수 있다. 더 복잡하게는 전역-지역 집계, 분할 고유 집계 같은 방안을 사용해볼 수 있다.

결함 내성

집계는 메모리에서 일어나므로 집계 노드에 장애가 생기면 집계 결과도 손실된다.
이 경우, 카프카 브로커에서 이벤트를 재수신해야 하는데, 이렇게 되면 시간이 오래걸린다. 따라서 업스트림 오프셋 같은 시스템 상태를 스냅숏으로 저장하고 마지막으로 저장된 상태부터 복구해 나가는 것이 바람직하다.

데이터 모니터링 및 정확성

지속적 모니터링
지연 시간, 메시지 큐 크기 지표 등의 경우에는 지속적으로 모니터링해야 한다.
조정
매일 각 파티션에 기록된 클릭 이벤트를 이벤트 발생 시각에 따라 정렬한 결과를 일괄 처리하여 만들어 낸 다음, 실시간 집계 결과와 비교해보면 정확성을 검증한다.

대안적 설계안

광고 클릭 데이터를 하이브에 저장한 다음 빠른 질의는 엘라스틱 서치를 이용해 처리해볼 수 있다. 집계의 경우 클릭하우스나 드루이드와 같은 OLAP 데이터베이스를 통해 처리할 수 있을 것이다.

4단계: 마무리

이번 장에서는 페이스북이나 구글 규모의 광고 클릭 이벤트 집계 시스템을 설계해보며 다음과 같은 주제를 다루었다.
데이터 모델 및 API 설계
맵리듀스 데이터 처리 패러다임을 통해 광고 클릭 이벤트를 집계하는 방안
메시지 큐, 집계 서비스, 데이터베이스의 규모 확장 방안
핫스판 문제를 해결하는 방안
시스템의 지속적 모니터링
데이터 조정을 통한 정확성 보증 방안
결함 내성
이 시스템은 전형적인 빅데이터 처리 시스템으로 아파치 카프카, 아파치 플링크, 아파치 스파크같은 업계 표준 솔루션에 대한 사전 지식을 구비해둔다면 구현이 용이할 것이다.