사전 지연 도착 데이터
문제

지연 도착 데이터

gabury1고친 사람 github-actions[bot]

지연 도착 데이터는 이미 집계를 마친 기간에 뒤늦게 끼어드는 데이터입니다. 일이 일어난 때보다 한참 늦게 들어와서 그 기간의 결과에서 빠집니다. 그래서 한번 내놓은 숫자가 아무 오류 없이 틀린 채로 남습니다. 늦게 온 몫을 넣어 그 기간을 다시 집계해야 숫자가 맞아집니다.

쉽고 빠른 이해

무슨 일인가 — 이미 계산을 끝낸 기간의 데이터가 뒤늦게 들어오는 일입니다. 어제 매출을 오늘 새벽 2시에 계산했습니다. 그런데 어제 밤 11시 50분에 난 결제 기록이 오늘 아침 9시에야 들어옵니다.

왜 곤란한가 — 새벽 계산에는 그 결제가 없었으니 어제 매출이 모자란 채 보고서에 남습니다. 오류가 나지 않아서 아무도 모르고 지나갑니다.

어떻게 생기나

  1. 데이터마다 일이 일어난 시각이 붙어 있습니다
  2. 정해진 때에 그 기간을 닫고 계산합니다
  3. 닫은 뒤에 그 기간에 일어난 데이터가 들어옵니다

대가 — 늦게 올 몫을 기다리면 결과가 늦어집니다. 바로 내고 나중에 다시 계산하면 일이 두 번 듭니다. 한번 내놓은 숫자도 바뀝니다.

상세

이 절은 데이터 한 건에 붙는 두 시각에서 시작해, 그 어긋남이 숫자를 틀리게 만드는 순서와 메우는 법까지 봅니다.

가계부 정산 비유

월말에 이번 달 가계부를 정산해 합계를 적었습니다. 그런데 사흘 뒤 지난달 말일에 쓴 카드 영수증이 하나 더 나옵니다.

이미 적은 합계는 그 영수증만큼 모자랍니다. 합계를 고치려면 지난달 장부를 다시 펴야 합니다.

일어난 시각과 들어온 시각

파이프라인은 여러 곳에서 데이터를 모아 정해진 순서로 가공하는 작업 흐름입니다. 결제 기록을 모아 하루 매출을 내는 일이 그런 예입니다. 파이프라인에 들어오는 데이터 한 건에는 시각이 둘 붙습니다.

첫째는 일이 일어난 시각입니다. 이 시각을 이벤트 시간이라고 부릅니다. 결제 버튼을 누른 순간, 센서가 온도를 잰 순간이 이벤트 시간입니다. 이 시각은 데이터 안에 타임스탬프로 적혀 옵니다.

둘째는 그 데이터가 파이프라인에 들어온 시각입니다. 이 항목에서는 도착 시각이라고 부릅니다. 데이터를 들어오는 대로 처리하는 쪽에서는 같은 것을 처리 시간이라고 부릅니다.

평소에는 두 시각이 거의 붙어 있습니다. 지연 도착 데이터는 이 차이가 크게 벌어진 데이터입니다. 그 기간의 계산이 끝난 뒤에야 도착합니다.

데이터가 늦는 까닭

데이터가 늦는 까닭은 여러 갈래입니다. 아래 표는 흔한 갈래를 예와 함께 놓습니다.

까닭 예
기기가 네트워크에 안 붙어 있다 비행기 안에서 쓴 앱 기록이 착륙한 뒤에 올라온다
보내다 실패해 [[재시도 다시 보낸다]]
보내는 쪽이 모아서 보낸다 거래처가 하루치 거래 파일을 다음 날 아침에 보낸다
앞 단계 작업이 늦었다 원본을 정리하는 앞 작업이 실패해 몇 시간 뒤에 다시 돈다
나중에 고쳐진다 취소와 환불이 며칠 뒤에 원래 결제 날짜로 들어온다

표의 까닭은 대부분 데이터를 보내는 쪽의 사정입니다. 파이프라인 쪽에서는 막을 수 없습니다. 그래서 지연 도착 데이터는 없애는 대상이 아니라 다루는 대상입니다.

숫자가 틀어지는 조건

집계는 기간마다 건수를 세거나 금액을 더해 숫자 하나로 줄이는 일입니다. 하루 매출, 시간당 접속 수가 집계의 결과입니다. 지연 도착 데이터는 이 집계를 틀리게 만듭니다.

이 문제는 아래 넷이 다 맞을 때 터집니다.

  1. 이벤트 시간으로 기간을 나눠 집계합니다. 하루 매출을 결제가 일어난 날짜로 셉니다
  2. 기간마다 정해진 때에 집계를 돌려 결과를 냅니다. 매일 새벽 2시에 어제 몫을 셉니다
  3. 집계를 돌린 뒤에, 이벤트 시간이 그 기간 안인 데이터가 들어옵니다
  4. 이미 집계한 기간을 다시 집계하는 장치가 없습니다

이미 집계한 기간을 다시 집계하는 장치가 있으면 숫자는 잠시 틀렸다가 맞아집니다. 집계 뒤에 들어오는 데이터를 없애려면 데이터가 다 올 때까지 집계를 미뤄야 합니다. 그런데 언제 다 오는지는 미리 알 수 없습니다.

하루 매출이 틀어지는 순서

매일 새벽 2시에 어제 매출을 세는 파이프라인을 봅니다. 결제 기록은 앱이 수집 서버로 보냅니다. 일별 집계는 수집 서버에서 하루 몫을 읽어 보고서에 적습니다.

10월 15일 밤 11시 50분에 난 결제 하나가 전송에 실패했다가 16일 아침 9시에 다시 들어옵니다.

sequenceDiagram
    participant 앱
    participant 수집 as 수집 서버
    participant 집계 as 일별 집계
    participant 보고서
    앱-x수집: 15일 23:50 결제 · 전송 실패
    집계->>수집: 16일 02:00 · 15일 몫을 읽는다
    집계->>보고서: 15일 매출을 적는다
    앱->>수집: 16일 09:00 · 다시 보낸다
    Note over 수집: 이벤트 시간은 15일 23:50
    Note over 집계,보고서: 15일 집계는 이미 끝났다

이 결제는 일어난 날로 치면 15일 몫입니다. 그런데 15일 집계가 돈 뒤에 도착했으니 보고서의 15일 매출에는 없습니다. 파이프라인은 정상으로 돌았습니다. 오류도 나지 않았습니다.

같은 일을 숫자로 옮기면 이렇습니다. 오른쪽 주석이 그 줄의 값입니다.

Python
day15 = [3000, 5000]
report = sum(day15)    # 8000
day15.append(4000)     # 16일 09:00 도착
sum(day15)             # 12000
report                 # 8000

원본에는 15일 결제가 12000 어치 쌓였습니다. 보고서의 15일 매출은 다시 집계하기 전까지 8000 으로 남습니다.

늦게 온 한 건이 들어가는 파티션

늦게 온 15일 결제 한 건을 15일 몫과 16일 몫 가운데 어디에 담느냐에 따라 틀어지는 모양이 갈립니다. 이 절은 앞의 새벽 2시 집계로 그 갈림을 봅니다.

새벽 2시 집계처럼 하루치 데이터를 모아 한 번에 돌리는 방식을 배치 처리라고 부릅니다. 결과는 정해진 때에 한꺼번에 나옵니다.

배치 처리는 데이터를 날짜별 파티션에 나눠 담는 일이 많습니다. 파티션은 큰 테이블을 날짜 같은 기준으로 잘라 따로 담은 조각입니다. 이렇게 나눠 두면 그날 몫만 골라 읽거나 새로 쓸 수 있습니다.

늦게 온 한 건을 담을 파티션의 기준은 이벤트 시간과 도착 시각 둘 중 하나입니다.

flowchart TD
    A["15일에 일어난 결제가 16일에 도착한다"] --> B{"파티션을 무엇으로 나누나"}
    B -->|이벤트 시간| C["15일 파티션에 들어간다"]
    C --> D["다시 집계하기 전까지 15일 매출이 모자란다"]
    B -->|도착 시각| E["16일 파티션에 들어간다"]
    E --> F["15일 매출은 계속 모자라고 16일 매출이 부푼다"]

이벤트 시간으로 나누면 15일 매출은 결국 맞아집니다. 다만 15일 파티션을 다시 집계하기 전까지는 모자랍니다.

도착 시각으로 나누면 다시 집계할 필요가 없습니다. 대신 15일 매출이 끝내 모자랍니다. 16일 매출은 그만큼 부풀어 있습니다.

날짜별 숫자를 맞춰야 하는 집계라면 이벤트 시간으로 나눕니다. 그리고 닫힌 파티션을 다시 집계하는 장치를 둡니다.

스트림 처리에서의 모습

스트림 처리는 데이터를 들어오는 대로 처리해 몇 초나 몇 분 안에 결과를 내는 방식입니다. 데이터에 끝이 없어서 「하루치가 다 모였다」는 때가 따로 오지 않습니다.

스트림 처리는 시간을 구간으로 잘라 구간마다 결과를 냅니다. 이 구간을 윈도라고 부릅니다. 10시부터 10시 5분 사이에 일어난 접속 수처럼 구간 하나가 결과 하나가 됩니다.

스트림 처리 엔진은 윈도를 언제 닫을지 워터마크로 정합니다. 워터마크는 「이 시각 이전에 일어난 데이터는 거의 다 왔다」는 엔진의 짐작입니다. 워터마크가 윈도의 끝을 지나면 엔진이 그 윈도의 결과를 냅니다.

워터마크가 지나간 뒤에 도착한 데이터를 스트림 처리에서는 늦은 데이터라고 부릅니다. 지연 도착 데이터가 스트림에서 보이는 모습입니다.

엔진은 윈도를 닫은 뒤에도 정해 둔 동안은 늦은 데이터를 받아 결과를 고칠 수 있습니다. 이 기간을 허용 지연이라고 부릅니다. 그 기간마저 지나서 온 데이터는 버리거나 따로 모아 둡니다.

배치와 다른 점은 닫는 때를 무엇이 정하느냐입니다. 배치는 시계가 새벽 2시가 되면 닫습니다. 스트림은 도착한 데이터의 이벤트 시간을 보고 닫습니다.

늦게 도착하는 차원

이번에는 늦게 오는 쪽이 주문이 아니라 고객 정보인 경우를 봅니다. 새 고객의 주문이 그 고객의 정보보다 먼저 들어오는 경우입니다. 이 일은 분석하려고 여러 곳의 데이터를 모아 둔 데이터 웨어하우스에서 자주 생깁니다.

웨어하우스는 주문 같은 사건을 적는 표와 그 사건을 설명하는 표를 나눠 두는 일이 많습니다. 사건을 적는 표를 팩트 테이블이라고 부릅니다. 주문 한 건이 한 행이 됩니다. 그 행에 금액과 함께 누가 샀는지를 고객 번호로 적습니다.

고객 이름이나 지역처럼 사건을 설명하는 정보는 차원 테이블에 따로 둡니다. 고객 한 명이 한 행이 됩니다. 이렇게 나눠 두면 주문마다 고객 이름을 되풀이해 적지 않아도 됩니다.

그런데 새 고객의 주문이 먼저 들어오면 그 고객의 정보는 아직 없을 수 있습니다. 그러면 주문을 이을 고객 행이 차원 테이블에 없습니다. 이처럼 차원 테이블의 행, 여기서는 고객 정보가 늦게 오는 경우를 늦게 도착하는 차원이라고 부릅니다.

흔한 대처는 내용이 빈 고객 행을 먼저 만들어 주문을 이어 두는 것입니다. 고객 정보가 나중에 오면 그 행을 채웁니다. 반대로 주문이 늦게 오는 경우는 앞의 하루 매출 예와 같은 문제입니다.

알아채는 법

지연 도착 데이터는 오류를 내지 않습니다. 알아채려면 숫자 몇 개를 따로 재서 봐야 합니다.

보는 것 알려 주는 것
이벤트 시간과 도착 시각의 차이 데이터가 보통 얼마나 늦게 오는가
같은 기간을 나중에 다시 센 값 처음 낸 숫자가 얼마나 모자랐나
집계를 돌린 뒤 그 기간 몫으로 들어온 건수 닫힌 기간에 늦게 온 데이터가 몇 건인가

첫째 줄의 차이를 모아 보면 기다릴 시간을 정하는 근거가 됩니다. 이 차이를 재려면 데이터마다 도착 시각을 따로 적어 두어야 합니다. 이벤트 시간만 남기면 늦게 왔는지를 나중에 알 길이 없습니다.

메우는 방법

고치는 길은 크게 둘입니다. 닫기 전에 더 기다리거나, 닫은 뒤에 다시 집계하는 것입니다.

방법 하는 일 대가
늦게 닫기 새벽 2시 대신 오후에 어제 몫을 센다 결과가 그만큼 늦게 나온다
지난 며칠을 매번 다시 집계 매일 어제뿐 아니라 그 전 사흘 몫도 다시 센다 같은 데이터를 여러 번 읽는다
늦게 온 날짜만 다시 집계 닫힌 기간에 데이터가 들어오면 그 날짜만 다시 돌린다 어느 날짜에 늦게 왔는지 따로 추적해야 한다
허용 지연 스트림에서 윈도를 닫은 뒤에도 정해 둔 동안 받는다 결과를 여러 번 내고, 받는 쪽이 고친 값을 반영해야 한다

표의 둘째 줄과 셋째 줄처럼 지난 기간을 다시 집계하는 일을 통틀어 재처리라고 합니다. 원본 데이터를 다시 읽어 그 기간의 결과를 새로 냅니다.

백필은 비거나 틀린 과거 기간을 골라 뒤늦게 채우는 작업입니다. 표의 셋째 줄이 그 예입니다. 15일 몫으로 결제가 늦게 들어오면 15일 하나만 골라 다시 채웁니다.

다시 집계는 몇 번을 돌려도 같은 결과를 내야 합니다. 늦게 온 데이터는 언제 또 올지 모르니 같은 날짜를 여러 번 다시 집계하게 되기 때문입니다. 몇 번을 돌려도 결과가 같은 이 성질이 멱등성입니다.

결과를 덧붙이는 식으로 다시 집계하면 같은 날짜의 숫자가 두 번 들어갑니다. 이런 일이 중복 적재입니다. 이를 막으려고 다시 집계할 때는 그 날짜의 파티션을 통째로 새로 써서 바꿉니다.

숫자가 확정되는 때

어느 방법을 골라도 숫자가 확정되는 때와 결과가 나오는 때가 서로 당깁니다. 일찍 내면 나중에 바뀝니다. 바뀌지 않게 하려면 늦게 내야 합니다.

그래서 처음 낸 숫자를 잠정치로 표시하는 곳도 있습니다. 며칠 뒤 다시 집계한 값이 나오면 확정치로 바꿉니다. 숫자를 받아 쓰는 사람이 그 숫자가 아직 바뀔 수 있다는 것을 알게 하려는 것입니다.

관련 항목

지연 도착 데이터를 가르는 두 시각

이벤트 시간 · 처리 시간 · 타임스탬프 · 시간과 시계 · 시계 동기화

늦은 데이터를 다루는 스트림 처리 개념

스트림 처리 · 윈도 · 워터마크 · 허용 지연 · 트리거 · 늦은 데이터 · Apache Flink · Apache Beam

늦게 온 몫을 메우는 처리 방식

백필 · 재처리 · 증분 처리 · 배치 처리 · 멱등성 · 파티셔닝 · 파티션

지연 도착 데이터와 함께 불리는 데이터 처리 장애

중복 적재 · 데이터 누락 · 작은 파일 문제 · 데이터 품질 · 태스크 실패 · 스키마 변경

늦게 도착하는 차원이 생기는 분석 모델

데이터 웨어하우스 · 차원 모델링 · 팩트 테이블 · 차원 테이블 · 집계

지연 도착 데이터가 속하는 상위 분류

데이터 엔지니어링 · 데이터 파이프라인 · ETL · 분산 시스템

다른 이름: late-arriving data · late arriving data · 늦게 도착하는 데이터 · 늦게 도착한 데이터