지금 또는 나중에
사전 계산(Precompute) 또는 온디맨드 컴퓨팅(Compute on demand)
데이터가 쓰여질 때 작업을 수행하여 읽기가 즉각적이 되지만 데이터가 오래될 수 있습니다. 아니면 데이터를 읽을 때 작업을 수행하여 결과가 신선하게 유지되지만, 모든 요청마다 비용이 발생합니다.
분산 시스템에 대한 작업 문서: 처음부터 구축된 개념 모듈, 해당 기술에 대한 페이지, 그리고 아키텍처 다이어그램, 트레이드오프(trade-offs), 실제로 질문받는 후속 질문까지 포함된 전체 디자인입니다.
또한 여기 코딩 트랙도 있습니다: 데이터 구조 및 알고리즘을 단계별로 그렸습니다. 사전 계산 또는 온디맨드 컴퓨팅
데이터가 쓰여질 때 작업을 수행하여 읽기가 즉각적이 되지만 데이터가 오래될 수 있습니다. 아니면 데이터를 읽을 때 작업을 수행하여 결과가 신선하게 유지되지만, 모든 요청마다 비용이 발생합니다.
속도와 가용성(Speed and availability), 일관성(consistency)으로 지불하다
레플리카(Replicas)와 캐시(caches)는 데이터를 리더에 더 가깝게 배치하고 장애를 겪어도 유지되지만, 추가되는 모든 복사본은 진실의 출처(source of truth)보다 뒤처질 수 있습니다. 진짜 질문은 제품이 얼마나 오래된 읽기를 허용할 수 있느냐입니다.
용량(Capacity), 조정(coordination)으로 지불하다
더 큰 머신은 모든 것을 단순하게 유지하지만, 한계에 부딪힐 때까지는 그렇습니다. 데이터를 여러 머신에 분산하고 트래픽을 나누면 그 한계를 제거하지만, 라우팅(routing), 리밸런싱(rebalancing), 핫 키(hot keys) 및 샤드(shards)를 가로지르는 쿼리가 추가됩니다.
분할(partition) 동안 포기하는 것들
네트워크가 분열될 때, 노드는 보장할 수 없는 요청은 거부하거나 오래된 데이터를 담아 응답합니다. 결제 및 좌석 재고는 일관성을 따르는 경향이 있고; 피드와 좋아요 카운트는 가용성을 따르는 경향이 있습니다.
새로운 프롬프트가 실제로 무엇에 관한 것인지 파악하고, 한쪽을 선택한 후 제품 요구사항으로 방어하세요. 그것이 테스트되는 기술입니다.
순서대로 읽으세요. 기초부터, 그리고 모든 것이 구축되는 패턴들부터.
지연 시간(Latency) 대 처리량(throughput), 백분위수 및 꼬리 증폭(tail amplification), 확장 방향, 로드 밸런싱(load balancing), 그리고 결정으로 끝나는 추정치.
접근 패턴에 따라 데이터베이스를 선택하는 방법, 인덱스가 실제로 비용을 발생시키는 방식, 복제 지연(replication lag), 샤딩 전략(sharding strategies), 그리고 일관성 해싱(consistent hashing).
CAP(일관성-가용성-분할 내성)은 정확하게 명시되었고, PACELC, 일관성 스펙트럼, 쿼럼(quorums), Raft, 반복 불변성(idempotency), 사가(sagas), 그리고 아웃박스 패턴(outbox pattern)에 대해 다룹니다.
캐시 패턴 및 제거 정책(eviction), 히트율이 지연 시간(latency)을 지배하는 이유, 무효화(invalidation), 그리고 각각의 해결책과 함께 언급되는 세 가지 실패 모드에 대해 설명합니다.
큐(Queues) 대 pub/sub, Kafka 파티션 및 컨슈머 그룹(consumer groups), 전달 의미론(delivery semantics), 지터가 적용된 재시도(retries with jitter), 데드 레터 큐(dead letter queues), CQRS와 이벤트 소싱(event sourcing)을 다룹니다.
푸시(Push) 대 풀(Pull), 읽기/쓰기 비용의 비대칭성, 셀러브리티 문제(celebrity problem), 그리고 이를 해결하는 하이브리드 방식에 대해 설명합니다. 이는 이 분야에서 가장 많이 재사용되는 패턴입니다.
REST, gRPC 및 GraphQL을 비교하고; WebSockets을 통한 폴링(polling); 커서 기반 페이지네이션(cursor pagination); API 게이트웨이(API gateways); 그리고 네 가지 속도 제한 알고리즘에 대해 다룹니다.
오류 예산(Error budgets), 장애 조치(failover) 및 스플릿-브레인(split-brain), 타임아웃(timeouts) 및 서킷 브레이커(circuit breakers), 우아한 저하(graceful degradation), 관측 가능성(observability), 그리고 안전한 스키마 마이그레이션에 대해 다룹니다.
역순 인덱스(Inverted indexes), 오브젝트 스토리지(object storage), 배치 처리 대 스트림 처리, 블룸 필터(Bloom filters), Snowflake ID, 그리고 지리 공간 인덱싱(geospatial indexing)을 다룹니다.
시간 예산이 주어진 45분 프레임워크와 개념에 매핑된 프롬프트, 점수를 받는 문장, 감점 요인, 그리고 평가 기준(rubric)에 대해 설명합니다.
실제 라운드에서 언급하게 될 시스템들. 각각의 정의, 강점, 그리고 적절한 답변이 되는 순간을 다룹니다.
관계형 데이터베이스의 기본: 트랜잭션(transactions), 조인(joins) 및 단일 주 키(primary key)에 대한 제약 조건으로 시작하여, 확장성 때문에 다른 방식으로 전환해야 하는 경우를 설명합니다.
유용한 자료 구조가 필요한 밀리초 미만의 메모리: 캐시, 카운터, 락, 리더보드, 그리고 속도 제한을 다룹니다.
단순히 가져오기(get)와 설정하기(set) 기능만 하는 캐시 — Redis보다 기능이 적다는 것이 오히려 장점입니다.
선형적으로 확장되는 마스터리스(Masterless), 쓰기 최적화 스토리지 — 모든 쿼리가 사전에 알려져 있다는 전제 하에 그렇습니다.
키-값 및 문서 스토리지로, 크기에 관계없이 일정한 지연 시간을 가지며, 키를 염두에 두고 설계하는 경우입니다.
관련성 순위(relevance ranking)가 있는 역순 인덱스 — 데이터의 파생된 뷰이며, 절대 진실의 원천(source of truth)이 아닙니다.
컬럼 기반 데이터베이스(Columnar databases) — ClickHouse, Druid, BigQuery — 수십억 개의 행을 집계하며 포인트 업데이트는 할 수 없습니다.
내구성이 뛰어나고 재사용 가능하며 파티셔닝된 로그: 거의 모든 비동기 설계의 핵심입니다.
무한 스트림에 대한 상태 저장 컴퓨팅: 충돌에도 살아남는 윈도우, 조인 및 집계 기능입니다.
바이트 단위의 S3 스타일 스토리지: 사실상 무제한이며, 극도로 내구성이 뛰어나고 요청 경로에 포함되지 않습니다.
지연 시간을 줄이고, 읽기 급증을 흡수하며, 대부분의 트래픽을 오리진에서 분산시키는 사용자 근처 캐시입니다.
프런트 도어: 모든 서비스마다 구현하는 대신 한 곳에서 TLS, 라우팅, 인증(auth), 속도 제한(rate limits) 및 타임아웃을 처리합니다.
서버 푸시를 위한 영구 연결 — 그리고 그 상태 저장 계층, 라우팅 및 재연결 시나리오가 따라오는 부분입니다.
HTTP/2 기반의 타입이 지정되고 이진화된 스트리밍 RPC: 엣지 뒤 서비스 간 호출의 기본 방식입니다.
클러스터가 합의해야 하는 결정에 대한 작고 강하게 일관된 저장소: 누가 리더인지, 누가 살아있는지, 누가 무엇을 소유하는지를 기록합니다.
자주 발생하는 문제들로 순위를 매겼습니다. 처음 다섯 가지는 질문되는 대부분의 내용을 다룹니다.
초당 15만 건의 읽기 요청이 발생하고 한 게시물이 수천만 명의 팔로워에게 도달할 수 있는 홈 타임라인입니다.
셀러브리티 문제(Celebrity problem). 중간 사용자에게만 작동하는 설계는 실패합니다. 팬아웃 비용은 이봉형이며 코드 경로는 분기해야 합니다.
5천만 개의 동시 소켓, 순서가 보장되는 메시지 전달, 그리고 대화 도중 오프라인 상태가 되는 사용자가 있습니다.
상태 저장 게이트웨이 노드가 50개 있고 Alice에게 보내야 할 메시지가 있다고 가정해 봅시다. 발신자는 그녀의 소켓을 보유한 노드를 어떻게 찾으며, 그녀가 오프라인일 때는 무슨 일이 발생할까요?
하루에 1억 개의 링크와 100 대 1의 읽기 편향(read skew)이 있습니다. 고전적인 추정치 워밍업 문제입니다.
중앙 병목 지점 없이 짧고, 고유하며, 예측 불가능한 키를 생성하는 것 — 그리고 이것이 데이터베이스 문제가 아니라 캐시 문제임을 인식해야 합니다.
수백만 요청/초로 무상태 API 서버 군에 걸쳐 전역 제한을 적용하는 것입니다.
모든 요청마다 동기식 Redis 왕복(round trip) 없이 전역 제한을 강제하고, 제한 장치 자체의 스토어가 다운되었을 때 무슨 일이 발생하는지 결정해야 합니다.
수많은 프로듀서로부터 발생하여 수신자에게 전달되며, 인앱(in-app), 이메일, 푸시 알림을 통해 손실 없이 배달되는 이벤트들.
제3자 배달 채널은 끊임없이 실패하고 속도 제한에 걸립니다. 아무것도 손실되어서는 안 되며, 눈에 띄게 중복되어도 안 되고, 하나의 불안정한 제공업체가 나머지 시스템을 다운시키면 안 됩니다.
10억 개의 문서, 초당 10만 건의 쿼리, 그리고 키 입력마다 작동하는 자동 완성 기능.
검색은 스캐터-개더(scatter-gather) 방식이므로 지연 시간(latency)은 가장 느린 샤드(shard)가 됩니다. 게다가 인덱스(index)는 진실의 원천(source of truth)이 아니므로, 어떻게 동기화 상태를 유지하는지 그리고 그것이 지연되는지를 설명해야 합니다.
4초마다 위치 정보를 게시하는 5백만 대의 운전자가 실시간으로 승객과 매칭되는 경우.
두 가지 문제가 결합된 것입니다: 초당 125만 건의 위치 쓰기 작업은 디스크 기반 인덱스를 파괴하며, 두 명의 승객이 같은 운전자를 받지 않도록 해야 하는 매칭 단계입니다.
초당 25 테라비트(terabits)의 외부 전송량으로 비디오를 업로드, 트랜스코딩하고 배달하는 작업.
비디오 바이트는 업로드 시나 재생 시에 여러분의 애플리케이션 서버에 절대 닿지 않습니다. 실제로 구축하는 것은 메타데이터 서비스와 트랜스코딩 파이프라인입니다.
100억 개의 페이지, 초당 1만 건의 가져오기(fetch) 작업이며, 특정 도메인 하나를 과부하시키지 않아야 합니다.
정중함. 빠르게 크롤링하는 것은 쉽지만, 한 호스트를 과부하에 걸리지 않게 빠르게 크롤링하려면 FIFO 방식보다는 호스트별로 그룹화된 큐 설계가 필요합니다. 그런 다음 볼 수 있는 것을 저장할 수 없는 규모에서 중복 제거(deduplicate)해야 합니다.
신뢰할 수 없는 외부 프로세서를 통해 발생하는 청구, 캡처, 환불 및 지급액 처리와 함께 장부를 균형 있게 유지해야 하는 경우.
일관성(consistency)이 가용성(availability)을 이기고, '최소 한 번 이상(at-least-once)'에 더해 '멱등성(idempotent)'이 슬로건이 아니라 이중 청구를 방지하는 메커니즘이 되는 유일한 설계.
같은 초에 같은 100개의 좌석을 원하는 5만 명의 사람들.
이것은 규모(scale) 문제가 아니라 경합(contention) 문제입니다. 행 잠금(Row lock contention)은 처리량(throughput)보다 먼저 깨지므로, 해결책은 더 큰 클러스터가 아니라 애플리케이션 계층 앞에서의 진입 제어(admission control)입니다.
한 단락이 변경되었다는 이유만으로 2기가바이트 파일을 다시 업로드하지 않고 기기 간에 파일을 동기화하는 작업.
콘텐츠 기반 청킹(content-defined chunking)과 델타 동기화(delta sync)를 통한 대역폭 효율성 — 그리고 동일한 파일을 오프라인으로 편집한 두 클라이언트를 위한 일관된 스토리.
초당 백만 건의 이벤트가 빠르고 정확한 과금 숫자를 가진 대시보드로 집계됩니다.
이벤트 시간. 클릭은 늦게, 순서에 어긋나게, 그리고 중복되어 도착합니다. 도착 시간을 기준으로 집계하는 것은 쉽지만 잘못되었으며, 광고주들은 이 수치로 청구됩니다.
Redis 구축: 서브 밀리초(sub-millisecond) 읽기를 제공하며 테라바이트 규모의 핫 데이터를 담는 백 개 노드.
재균형화(Rebalancing). 단순 모듈러 해싱(modulo hashing)은 노드를 추가할 때 캐시의 80%를 무효화하고 원본 시스템에 과부하를 일으킵니다. 그리고 일관성 해싱(consistent hashing)조차도 핫 키(hot keys) 문제를 해결하지 못합니다.
한 문서에 여러 사람이 동시에 타이핑하는 상황에서, 인지 가능한 지연이나 편집 누락 없이 작업이 가능해야 합니다.
수렴(Convergence). 두 사용자가 협업 없이 같은 문장을 편집할 때. 둘 다 동일한 문서로 끝나야 하며 어느 쪽의 편집도 조용히 손실되어서는 안 됩니다 — 따라서 마지막 쓰기 승리(last-write-wins) 방식은 치명적으로 잘못되었습니다.