데이터베이스 5편 - 정확성의 재정의, 완벽함이라는 비용
Published: May 27, 2026
2024년, Canva의 크리에이터 정산 시스템에서는 월 1회 이상 장애가 발생하고 있었다. 크리에이터의 수입과 직결되는 수십억 건의 콘텐츠 사용량을 MySQL로 한 건씩 정확히 세고 있었기 때문이다. 트래픽이 18개월마다 두 배로 뛰면서 과다 집계, 누락, 분류 오류가 반복됐고, 문제가 터질 때마다 엔지니어가 DB에 직접 접속해 깨진 데이터를 수동으로 고쳐야 했다.
결국 Canva는 실시간 집계를 버렸다. 매 건을 즉시 세는 대신, 한 달치 데이터를 통째로 끌어와 몇 분 만에 재계산하는 구조로 전환했다. 그 결과 코드량은 절반으로, 장애는 수개월에 한 번으로 줄었다. 매 순간의 정확성을 완화하자 최종 결과의 정확성이 오히려 올라간 것이다.
완벽한 정확성을 실시간으로 유지하려는 시도가 시스템을 잠식하고 있었다. Canva가 살아남은 방법은 더 뛰어난 기술의 도입이 아니라, 정확성의 기준 자체를 재정의하는 판단이었다.
이것은 경영에서도 낯선 문제가 아니다. 매 시간 전 부서에 재고 전수조사를 지시하는 CEO는 정확한 숫자를 얻기도 전에 조직을 먼저 마비시킨다. 현장이 보고서 작성에 매몰되어 다른 업무를 처리할 시간이 사라지기 때문이다.
대규모 시스템은 모든 처리에서 정합성을 완벽하게 유지하는 구조가 아니다. 이번 편은 그 판단의 기준을 다룬다. 어떤 데이터에 절대적 정확성을 부여하고, 어떤 데이터에 근사치와 시차를 허용할 것인가.
Reference: Facebook
Reference: Canva
데이터마다 요구하는 정확도
섹션 제목: “데이터마다 요구하는 정확도”4편에서 시스템은 중앙 통제를 해체했다. 읽기 권한을 복제 노드에 위임했고, 데이터를 여러 샤드로 분할했으며, 중앙 서버 하나가 모든 요청을 통제하던 구조를 내려놓았다.
하지만 분산 이후 새로운 문제가 등장한다. 개발자는 수많은 데이터와 요청, 그리고 쏟아지는 동시 조회 속에서 깨닫게 된다. 문제는 저장 공간이 아니라, 모든 것을 실시간으로 정확하게 파악하는 것에서 발생하는 부하이다.
유튜브 동영상의 조회수, 실시간 인기 랭킹, 인스타그램의 추천 피드나 광고 클릭 통계가 대표적이다. 사용자가 영상을 클릭할 때마다 전 세계에 분산된 데이터베이스 장부에 실시간으로 ‘+1’을 완벽하게 동기화하려 한다면, 시스템은 정합성 비용을 버티지 못한다.
분산 환경에서는 네트워크 단절과 노드 간 상태 불일치가 언제든 발생할 수 있다. 모든 서버가 항상 동일한 진실을 공유하는 것은 현실적으로 불가능에 가깝다. 어디까지 일관성을 유지하고, 어디서부터 속도와 가용성을 우선시할 것인가. 데이터마다 요구하는 정확도의 기준을 비즈니스에 맞게 개별적으로 조절할 필요가 있다.
완벽한 정보의 대가
섹션 제목: “완벽한 정보의 대가”부장님이 “지금 동접자 몇 명이야?”라고 1초마다 물어본다고 치자. 그때마다 전 직원이 일을 멈추고 사람을 세어야 한다면? 그 회사는 제품은 하나도 못 팔고 보고만 하다가 파산한다. 완벽한 최신 정보를 실시간으로 쥐어짜는 행위는 운영 가능한 수준을 무너뜨릴 만큼 비싸다.
Canva가 겪은 문제는 특수한 사례가 아니다. 서비스가 성장하면 누구나 부딪히는 벽이다. 실시간 조회수, 좋아요 순위, DAU 집계, 인기 검색어, 추천 점수. 처음에는 단순한 쿼리 한 줄이었던 것들이, 트래픽이 커지면서 시스템 전체의 지연으로 누적된다.
실시간 집계가 왜 비싼가
섹션 제목: “실시간 집계가 왜 비싼가”조회 한 건을 가져오는 것은 쉽다. 인덱스가 있으면 DB는 이미 그 데이터의 위치를 알고 있기 때문이다. 2편에서 다뤘듯이, 인덱스는 수억 건의 데이터에서도 특정 한 건을 몇 번의 디스크 접근만으로 찾아낸다.
문제는 집계다. COUNT, GROUP BY, ORDER BY는 특정 한 건을 찾는 작업이 아니다. 조건에 해당하는 모든 행을 훑으며 상태를 처음부터 다시 계산하는 작업이다. 인덱스가 아무리 정교해도 “전체를 세라”는 요청 앞에서는 지름길이 없다. 데이터가 커질수록 계산 범위가 함께 커진다. 조회는 찾기지만, 집계는 매번 다시 세기다.
초당 수천 명이 동시에 문을 두드리는 순간, 데이터베이스는 저장소가 아니라, 같은 숫자를 끝없이 계산하는 실시간 계산기로 변한다.
중요도에 따른 데이터 레벨링
섹션 제목: “중요도에 따른 데이터 레벨링”물론 대규모 시스템은 정확성을 아예 포기하지 않는다. 대신 정확성을 계층화한다.
모든 데이터를 동일한 수준으로 실시간 정확하게 유지하려는 순간, 계산 부담은 시스템 규모와 함께 폭발하기 때문이다. 결국 시스템은 각 데이터가 어디까지 정확해야 하는가를 구분해야한다.
- 계좌 잔액 : 단 1원도 틀리면 안 된다
- 결제 상태 : 성공과 실패가 즉시 일치해야 한다
- 조회수 : 몇 초 늦게 반영되어도 문제없다
- 추천 피드 : 대략 맞으면 충분하다
- 광고 통계 : 오차 1%는 현실적으로 허용 가능하다
핵심은 모든 데이터에 동일한 기준을 적용하지 않는 것이다.
절대적 정확성이 필요한 영역은 높은 부하을 감수하고 끝까지 정합성을 유지한다. 반면, 비즈니스적으로 약간의 오차가 허용되는 영역은 캐시, 비동기 집계, 근사 알고리즘 같은 방식으로 점차 계산 비용을 줄여 나간다.
대규모 시스템의 확장은 결국 정확성을 제거하는 과정이 아니라, 정확성이 정말 필요한 곳에만 집중시키는 과정에 가깝다.
첫번째 전략 - 캐시라는 타협
섹션 제목: “첫번째 전략 - 캐시라는 타협”대표님이 매출을 물어볼 때마다 전국 지점장에게 전화를 돌리는 비서는 없다. 차라리 오늘 아침에 미리 뽑아둔 ‘일일 보고서(캐시)‘를 서랍에서 꺼내는 게 낫다. 그게 대표님의 성질도 죽이고, 모두의 목숨도 살리는 유일한 길이다.
대규모 시스템도 같은 선택을 하기 시작한다. 약간의 지연이나 오차가 치명적이지 않은 영역부터 시스템은 “완벽한 현재 상태”를 포기하고, 이미 계산된 결과를 재사용하기 시작한다.
이 지점에서 등장하는 첫 번째 전략이 캐시(Cache)다.
왜 캐시가 존재하는가
섹션 제목: “왜 캐시가 존재하는가”시스템은 같은 계산을 계속 반복하는 부하을 감당하기 어렵다. 캐시는 그 상황을 모면하기 위한 첫 번째 타협이다. 최신 상태를 매번 새로 계산하는 대신, 방금 계산한 결과를 잠시 재사용하기 시작한 것이다. 원본 데이터베이스로 향하던 읽기 요청을 앞단에서 흡수하여, 데이터베이스가 매번 현재 상태를 다시 세는 부담을 내려놓게 만든다.
Facebook 역시 같은 문제를 피해갈 수 없었다. 수십억 명의 복잡한 관계망을 매번 데이터베이스에서 직접 조회하고 연산하는 구조는 한계가 명확했다. 자주 조회되는 데이터를 메모리에 유지하는 캐시 계층(TAO)을 구축한 것은 기술적 과시가 아니라, 초당 수십억 건의 읽기 요청 앞에서 데이터베이스를 생존시키기 위한 선택이었다.
이제 시스템은 찰나의 완벽한 최신 원본을 고집하는 대신, 비즈니스에 지장이 없을 만큼 “충분히 최신인 복사본”을 타협안으로 쥐고 거대한 트래픽 속에서 숨통을 틔운다.
Reference: TAO: Facebook’s Distributed Data Store for the Social Graph (USENIX ATC ‘13)
캐시 전략의 세 갈래
섹션 제목: “캐시 전략의 세 갈래”하지만 단순히 복사본을 만드는 것으로 끝나지 않는다. 데이터를 언제 메모리에 올릴 것인지, 그리고 원본 데이터가 변경되었을 때 어떤 시점에 동기화할 것인지에 따라 감당해야 하는 대가가 달라진다. 결국 시스템은 최신성, 속도, 유실 위험 사이에서 무엇을 우선할지 선택해야 한다.
-
Cache Aside
Request ─▶ Cache ─ (MISS) ─▶ DB│└────▶ Cache가장 일반적인 방식이다. 평소에는 캐시만 조회하다가, 데이터가 없으면 그때서야 데이터베이스에 가서 장부를 확인하고 그 결과를 캐시에 적어둔다. 구조가 단순하지만, 캐시가 만료되는 순간 뒤이어 올 트래픽이 다시 데이터베이스로 집중되는 어려움을 감수해야한다.
-
Write Through
Write ───┬──▶ Cache│└──▶ DB조회 속도보다 “지금 보고 있는 값이 진짜 최신인가”라는 불안을 줄이는 게 더 중요한 순간이 있다. Write Through는 그 정합성을 보장하기 위해 등장한 방식이다. 데이터를 새로 저장하거나 수정할 때 원본 DB와 캐시 메모리에 동시에 기록한다. 캐시가 항상 완벽한 최신 상태를 유지하지만, 매번 두 곳에 동시에 써야 하므로 쓰기 지연이라는 비용을 감당해야 한다.
-
Write Back
Write ──▶ Cache ── (later) ──▶ DB반대로 쓰기 지연을 거의 없애는 대신, 데이터 유실 리스크를 감수하는 거래다. 쓰기 요청을 반응이 가장 빠른 캐시에 먼저 기록하고, 원본 DB에는 나중에 모아서 비동기로 밀어 넣는다. 쓰기 지연은 사실상 사라지지만, DB에 반영되기 전에 캐시 서버가 다운되면 데이터가 영원히 사라지는 치명적인 위험을 안고 간다.
결국 세 전략 중 완벽한 정답은 없다. 읽기 효율, 쓰기 속도, 데이터 최신성, 그리고 유실 리스크라는 네 가지 축 위에서, 현재 시스템이 어떤 대가를 지불할 준비가 되어 있는가에 따라 선택이 갈릴 뿐이다.
캐시가 만드는 새로운 장애
섹션 제목: “캐시가 만드는 새로운 장애”하지만 캐시가 무너지는 순간, 시스템은 오히려 캐시가 없을 때보다 더 위험한 상태에 직면한다. 평소에 캐시 뒤에 숨어 있던 데이터베이스는, 폭발적인 트래픽을 직접 받아낼 체력을 기르지 못했기 때문이다.
- Cache Stampede (캐시 쇄도) : 인기 데이터의 캐시가 만료되면, 그동안 캐시 뒤에 숨어 있던 요청이 동시에 데이터베이스로 몰린다. 평소에는 문제가 없던 시스템도, 이때 만큼은 쏟아진 재계산 요청으로 인해 마비된다. 4편에서 다뤘던 요청 병합(Request Collapsing)이 현상을 단 한 건의 대표 쿼리로 압축해 내는 해법이 된다.
- Thundering Herd : 특정 이벤트를 기다리며 대기하던 수많은 요청이나 스레드들이 동시에 깨어나며 폭발적인 부하를 일으킨다. 이로 인해 캐시 재생성이나 락 해제와 같은 신호와 함께 일제히 자원을 선점하려 하면서 시스템 인프라가 연쇄적으로 흔들리게 된다.
여기서 분산 시스템의 기묘한 역설이 드러난다. 캐시는 평소에 부하를 흡수하며 시스템을 보호한다. 그러나 역설적이게도 만료와 재생성의 타이밍이 겹칠 때, 오히려 트래픽을 한곳으로 집중시키는 가장 위험한 병목 지점이 된다.
두번째 전략 - 근사치라는 대안점
섹션 제목: “두번째 전략 - 근사치라는 대안점”캐시는 “최신 데이터”의 기준을 느슨하게 만들어 부담을 줄였다. 하지만 여전히 정확한 값을 캐시에 저장하고 있다는 점은 변하지 않는다.
여기서 시스템은 한 단계 더 과감한 질문을 던지기 시작한다.
“정말 모든 값을 끝까지 정확하게 계산해야 할까?”
예를 들어 타운홀 미팅 다과를 준비할 때, 총무팀은 직원 5,000명의 출근 여부를 일일이 조사하지 않는다. 지난 행사 데이터를 기준으로 대충 이 정도면 되겠다를 계산한다. 약간의 오차를 감수하는 편이 훨씬 싸기 때문이다.
대규모 시스템도 비슷한 선택을 한다. 일부 영역에서는 아주 작은 오차를 허용하는 대신 계산 과정 자체를 극단적으로 줄이기 시작한다. 이 흐름 속에서 등장한 대표적인 전략들이 블룸 필터(Bloom Filter)와 하이퍼로그로그(HyperLogLog) 같은 확률 기반 자료구조다.
Bloom Filter
섹션 제목: “Bloom Filter”의외로 대규모 트래픽의 상당수는 “애초에 존재하지 않는 데이터”를 찾는 요청이다. 없는 사용자 ID 조회, 삭제된 게시글 접근, 잘못된 상품 번호 요청 같은 것들이다. 문제는 시스템이 이 데이터가 진짜 없는지 파악하기 위해 데이터베이스까지 조회를 해야만 “없음”을 확인할 수 있다는 점이다. 실패할 요청을 확인하는 과정에도 똑같이 값비싼 디스크 접근과 연산 비용이 지불된다.
블룸 필터는 이 구조적인 헛수고를 제거하기 위해 등장했다. 데이터베이스로 향하는 길목 맨 앞단에서 이 데이터는 확실히 없다를 미리 판정해 돌려보내는 방식이다.
요청 ──▶ [ Bloom Filter ] ──┬──▶ 없음 (확정) ──▶ 차단 │ └──▶ 존재 가능성 ──▶ DB물론 완벽하진 않다. 실제로는 없는데 있다고 판단하는 약간의 오차는 허용한다. 하지만 반대의 경우는 없다. “없다”고 판정된 데이터는 단 하나의 예외도 존재하지 않는다.
HyperLogLog
섹션 제목: “HyperLogLog”DAU(일간 활성 사용자) 같은 지표를 정확히 계산하는 과정도 마찬가지다. 수억 명 규모의 서비스에서 중복을 완전히 제거해가며 사용자를 한 명 한 명 정확히 세려면 막대한 메모리와 연산 부하가 든다.
정확 계산 - 1억 2,345만 6,789명 집계
메모리 / 연산 비용████████████████████████████████████
│ └──▶ 모든 사용자 추적 └──▶ 완전한 중복 제거 └──▶ 높은 메모리 사용 └──▶ 높은 연산 비용하이퍼로그로그는 정밀함을 포기하고, 확률적 추정으로 전체 개수를 가늠하는 방식을 취한다. 고작 12KB 남짓한 메모리만 쓰는 대신, 1% 내외의 작은 오차를 허용하는 거래다.
HyperLogLog - 약 1억 2천만 명 집계
메모리 / 연산 비용████████████████
│ └──▶ 확률적 추정 └──▶ ±1% 오차 허용 └──▶ 약 12KB 메모리 └──▶ 매우 빠른 계산대부분의 비즈니스에서 중요한 것은 오늘의 DAU가 정확히 1억 2,345만 6,789명인지가 아니다. 전체적인 지표가 상승 곡선인지, 어제에 비해 얼마나 변했는지 추세를 파악하는 것으로 충분하기 때문이다.
비즈니스가 실제로 원하는 것
어제 ↗오늘 ↗↗내일 ↘
= 완벽한 숫자보다 전체 추세 변화 파악근사치는 정확성을 완전히 버리는 기술이 아니다. 비즈니스 판단에 영향 없는 영역에서만 계산 비용을 극단적으로 줄이는 선택에 가깝다.
세번째 전략 - 스키마 포기
섹션 제목: “세번째 전략 - 스키마 포기”신사업을 추진할 때 본사의 엄격한 결재 양식과 절차를 그대로 따르다가는 시장 진입 타이밍을 놓치기 쉽다. 이때는 격식보다 유연함이 우선이다.
세 번째 전략도 비슷하다. 데이터를 담는 틀의 엄격함을 내려놓고, 변화에 유연하게 대응하는 길을 택하는 것이다.
RDB의 한계
섹션 제목: “RDB의 한계”관계형 데이터베이스(RDB)의 강점은 엄격한 스키마와 관계 중심 설계다. 구조가 명확하고 정합성을 유지하기 쉽다.
관계형 DB
User A ─┐User B ─┼──▶ [ id | name | age | email ]User C ─┘하지만 서비스의 변화 속도가 시스템의 규모와 한계를 넘으면, 이 완벽한 규칙은 오히려 병목이 된다. 수억 건 규모의 데이터베이스에서 필드 하나를 추가하기 위해 ALTER TABLE을 실행하는 작업은 그 자체로 거대한 운영 리스크다. 데이터 구조의 유효성을 테이블 전체 수준에서 일일이 검증하는 부담이 너무 커지기 때문이다.
게다가 현실의 데이터는 점점 더 예측 불가능하다. 사용자마다 프로필 구조가 다르고, 피드 데이터는 수시로 바뀌며, 요구하는 속성도 제각각이다. 이 모든 것을 하나의 고정된 테이블 틀 안에 억지로 밀어 넣으려 하면, 더 많은 컴퓨터 자원을 소모하는 역전 현상을 마주한다.
비정형 데이터베이스 NoSQL
섹션 제목: “비정형 데이터베이스 NoSQL”수십 년간 검증된 ‘행(Row)’ 단위를 벗어난 이유가 여기에 있다. 규격 없는 ‘문서(Document)‘나 키-값 형태를 택한 것은 기술적 우위 때문이 아니다. 시스템이 모든 데이터를 하나의 규칙 안에 묶어두는 부담을 더는 감당할 수 없었기 때문이다.
결국 시스템은 중앙의 일괄 통제를 완화했다. 모든 데이터를 검증하고 동기화하던 질서를 내려놓은 것이다. 대신 데이터 단위마다 독립적인 구조를 허용했다.
비정형 DB
User A ─────▶ { name, age, hobby }User B ─────▶ { name, email }User C ─────▶ { name, location, job, github }물론 트레이드 오프도 명확하다. 테이블 간의 연결(JOIN)은 약해지고, 동일한 데이터가 사방에 중복 저장되며, 시스템 전체의 강한 정합성을 유지하기는 한층 까다로워진다.
하지만 이는 기술의 우열 문제가 아니다. 대규모 시스템의 확장은 더 완벽한 구조를 만드는 과정이 아니다. 변화하는 환경에 맞춰 어떤 질서를 끝까지 유지하고, 어떤 질서를 내려놓을 것인가를 끊임없이 저울질하는 과정에 가깝다.
만족 가능한 해
섹션 제목: “만족 가능한 해”여기까지의 모든 전략은 하나의 공통된 패턴을 공유한다. 완벽함의 일부를 의도적으로 내려놓고, 그 대가로 규모와 속도를 얻는 거래다.
흥미로운 건, 이런 선택이 분산 시스템에서만 나타나는 특수한 현상이 아니라는 점이다.
1978년 노벨 경제학상을 수상한 허버트 사이먼은 조직의 의사결정에 대해 날카로운 관찰을 남겼다. 인간과 조직은 모든 정보를 수집할 수 없고, 모든 경우의 수를 계산할 수도 없으며, 완벽한 최적해를 도출하는 것은 불가능하다.
그래서 현실의 조직은 최적해(optimal solution)를 추구하지 않는다. 대신 만족 가능한 해(satisficing)를 찾는다. 완벽한 답을 찾느라 정체되는 것보다, 적당히 괜찮은 답으로 움직이는 것이 생존에 유리하기 때문이다.
Reference: Herbert Simon, “Models of Bounded Rationality” (1982)
이번 편에서 다룬 모든 기술이 정확히 이 원리 위에 서 있다.
캐시는 매 순간 최신 상태를 보장하는 대신, 약간 늦더라도 빠르게 응답하는 길을 택했다. 블룸 필터(Bloom Filter)는 존재 여부를 끝까지 검증하기보다, 대부분의 불필요한 조회를 앞단에서 걸러내는 쪽에 집중했다. 하이퍼로그로그(HyperLogLog)는 모든 사용자를 정확히 세지 않고, 작은 오차를 감수하는 추정 방식을 받아들였다. NoSQL 역시 모든 데이터를 하나의 관계 질서 안에 묶기보다, 변화에 유연하게 대응할 수 있는 저장 방식을 선택했다.
어느 것도 완벽함에 도달하지 못한 것이 아니다. 완벽함을 추구하다가 치러야 할 계산 비용과 지연의 리스크를 계산해 본 뒤, 이를 의도적으로 내려놓은 것이다. 결국 분산 시스템도 인간 조직과 마찬가지로, 완벽함보다 지속 가능한 타협 위에서 움직이게 된다.
양보의 기술
섹션 제목: “양보의 기술”결국 확장의 핵심은 무엇을 더 잘할 것인가가 아니라, 무엇을 양보할 것인가를 결정하는 조율 능력이다.
- 어떤 데이터는 끝까지 정확해야만 한다
- 어떤 데이터는 대략적인 근사치면 충분하다
- 어떤 데이터는 몇 초 뒤에 반영되어도 괜찮다
- 어떤 데이터는 중복 저장을 허용해도 무방하다
이 경계선을 긋는 판단이 대규모 시스템 설계의 본질이다.
The Bottom Line
섹션 제목: “The Bottom Line”결국 시스템을 확장한다는 것은 기술적 완벽주의를 고수하는 일이 아니다. 비즈니스의 생존을 위해 어디까지 눈을 감아줄 수 있는지 결정하는 현실적인 타협 과정이다. 완벽이라는 강박을 내려놓으면, 시스템은 비로소 무한히 확장할 수 있는 자유를 얻는다.
캐시는 최신성의 기준을 조율했고, Bloom Filter는 존재 확인의 완벽함을 덜어냈으며, HyperLogLog는 정확한 개수를 타협했다. 그리고 NoSQL은 관계형 데이터베이스가 끝까지 사수하려던 질서의 일부를 내려놓았다.
대규모 시스템의 확장은 완벽함을 강화하는 과정이 아니라, 어디까지 정확성을 유지할 것인가를 선택하는 과정에 가깝다.
다음 편: 이렇게 사방으로 흩어지고 느슨해진 시스템이 다시 단 하나의 거래를 완결 지어야 하는 모순의 순간을 다룬다. 결제는 성공했는데 재고 차감에 실패한다면? 쿠폰은 소멸했는데 주문이 취소된다면? 비즈니스가 다시 하나의 결과를 요구하는 상황, 분산 트랜잭션이다.