Saltar a contenido

06 - Casos de Estudio: Diseño de Sistemas Reales

Tabla de Contenidos


Metodología de Diseño

Antes de abordar cualquier diseño de sistema, sigue estos 4 pasos:

Paso 1: Clarificar Requisitos (5 min)

Funcionales: ¿Qué hace el sistema? No funcionales: Escala, latencia, disponibilidad, consistencia

Preguntas clave: - ¿Cuántos usuarios? ¿DAU (daily active users)? - ¿Ratio lectura/escritura? - ¿Qué latencia es aceptable? - ¿Qué disponibilidad se necesita? - ¿Consistencia fuerte o eventual?

Paso 2: Estimaciones de Escala (5 min)

Calcular: QPS (queries per second), almacenamiento, ancho de banda.

Paso 3: Diseño de Alto Nivel (15 min)

Diagrama de componentes principales y flujos de datos.

Paso 4: Diseño Detallado (15 min)

Profundizar en los componentes más críticos o interesantes.


Caso 1: Diseñar Twitter

Paso 1: Requisitos

Funcionales: - Publicar tweets (280 caracteres + media) - Timeline personal (tweets de las cuentas que sigues) - Buscar tweets - Seguir/dejar de seguir usuarios - Like y retweet

No funcionales: - 500M usuarios, 200M DAU - Un usuario promedio sigue a 200 cuentas - 500M tweets/día (~6000 tweets/seg) - Timeline: lectura pesada (ratio 100:1 read/write) - Latencia de timeline < 200ms - Disponibilidad: 99.99%

Paso 2: Estimaciones

Escrituras (tweets):
  500M tweets/día = ~6,000 tweets/seg
  Tamaño promedio tweet: 280 bytes texto + metadata = ~1 KB
  Storage/día: 500M × 1KB = 500 GB/día (solo texto)
  Con media (10% con imagen/video): +50M × 500KB = 25 TB/día

Lecturas (timeline):
  200M DAU × 5 timeline refreshes/día = 1B requests/día
  ~12,000 reads/seg
  Cada timeline muestra ~200 tweets → data transfer significativo

Relaciones (follows):
  200M usuarios × 200 follows promedio = 40B relaciones

Paso 3: Diseño de Alto Nivel

graph TD
    subgraph "Clientes"
        MOB[Mobile App]
        WEB[Web App]
    end

    MOB --> LB[Load Balancer]
    WEB --> LB
    LB --> GW[API Gateway]

    GW --> TS[Tweet Service]
    GW --> TLS[Timeline Service]
    GW --> US[User Service]
    GW --> SS[Search Service]

    TS --> TDB[(Tweet Store<br/>MySQL Sharded)]
    TS --> MQ[Message Queue<br/>Kafka]
    TS --> MEDIA[Media Store<br/>S3 + CDN]

    MQ --> FO[Fan-out Service]
    FO --> CACHE[(Timeline Cache<br/>Redis Cluster)]

    TLS --> CACHE

    US --> UDB[(User Store<br/>MySQL)]
    US --> GRAPH[(Social Graph<br/>Follow relationships)]

    SS --> ES[(Search Index<br/>Elasticsearch)]
    TS --> ES

Paso 4: Diseño Detallado

El Problema del Fan-out (El Corazón de Twitter)

Cuando un usuario publica un tweet, sus seguidores deben verlo en su timeline. Hay dos estrategias:

Fan-out on Write (Push Model)

Cuando se publica un tweet, se escribe inmediatamente en el cache de timeline de cada seguidor.

sequenceDiagram
    participant User_A as User A (1000 followers)
    participant TweetSvc as Tweet Service
    participant Kafka
    participant FanOut as Fan-out Workers
    participant Redis as Timeline Caches

    User_A->>TweetSvc: POST /tweet "Hello World"
    TweetSvc->>TweetSvc: Guardar en Tweet Store
    TweetSvc->>Kafka: Publicar evento TweetCreated
    Kafka->>FanOut: Consumir evento
    FanOut->>FanOut: Obtener lista de 1000 followers
    FanOut->>Redis: LPUSH timeline:follower_1, tweet_id
    FanOut->>Redis: LPUSH timeline:follower_2, tweet_id
    Note over FanOut,Redis: ... repite para 1000 followers ...
    FanOut->>Redis: LPUSH timeline:follower_1000, tweet_id

Ventaja: Lectura de timeline es ultra rápida (solo leer de Redis). Desventaja: Un usuario con 50M de seguidores genera 50M de escrituras. Lento y costoso.

Fan-out on Read (Pull Model)

Cuando un usuario abre su timeline, se consultan los tweets recientes de todas las cuentas que sigue.

sequenceDiagram
    participant User_B as User B (follows 200 accounts)
    participant TimelineSvc as Timeline Service
    participant Redis as Tweet Caches
    participant TweetDB as Tweet Store

    User_B->>TimelineSvc: GET /timeline
    TimelineSvc->>TimelineSvc: Obtener lista de 200 followed accounts
    TimelineSvc->>Redis: GET tweets de account_1
    TimelineSvc->>Redis: GET tweets de account_2
    Note over TimelineSvc,Redis: ... 200 queries paralelas ...
    TimelineSvc->>TimelineSvc: Merge + Sort por timestamp
    TimelineSvc-->>User_B: Top 200 tweets ordenados

Ventaja: Publicar un tweet es instantáneo (solo 1 write). Desventaja: Lectura lenta (200+ queries y merge).

Enfoque Híbrido (Lo que hace Twitter)
graph TD
    TWEET[Nuevo Tweet] --> CHECK{¿El autor tiene<br/>> 500K followers?}
    CHECK -->|No - Usuarios normales| PUSH[Fan-out on Write<br/>Push a timeline caches]
    CHECK -->|Sí - Celebridades| PULL[Fan-out on Read<br/>Merge en lectura]

    TIMELINE[Request de Timeline] --> MERGE[Merge]
    PUSH --> REDIS[(Redis Timeline<br/>Pre-computado)]
    REDIS --> MERGE
    PULL --> CELEB[(Tweets de celebridades<br/>Query on-the-fly)]
    CELEB --> MERGE
    MERGE --> USER[Timeline del usuario]
  • Usuarios normales (99%): Fan-out on Write (push)
  • Celebridades (>500K followers): Fan-out on Read (pull, merge en lectura)
  • El timeline final es un merge de ambas fuentes

Tweet Storage: Sharding

Shard Key: tweet_id (snowflake ID)

Snowflake ID (64 bits):
| 41 bits: timestamp | 10 bits: machine ID | 12 bits: sequence |
| ~69 años            | 1024 máquinas        | 4096/ms/máquina   |

Ventajas del Snowflake ID: - Ordenable por tiempo (range queries eficientes) - Generado sin coordinación central - Único globalmente

Search: Inverted Index

Índice invertido en Elasticsearch:

"hello"   → [tweet_1, tweet_45, tweet_892]
"world"   → [tweet_1, tweet_203]
"design"  → [tweet_45, tweet_567, tweet_892]

Query: "hello world" → intersección → [tweet_1]

Social Graph

Las relaciones follow/following se modelan como un grafo dirigido:

Opción 1: Tabla relacional
  follows(follower_id, followed_id, created_at)
  Índices: (follower_id) para "¿a quién sigo?" y (followed_id) para "¿quién me sigue?"

Opción 2: Graph database (para queries complejas)
  "Amigos de amigos que siguen a X"
  "Sugerencias basadas en grafo social"

Opción 3: Adjacency list en Redis
  followers:user_123 → SET {user_1, user_2, user_3}
  following:user_123 → SET {user_50, user_51}

Caso 2: Diseñar Netflix

Paso 1: Requisitos

Funcionales: - Subir y procesar videos (content team) - Catálogo de contenido con búsqueda - Streaming de video adaptativo (múltiples calidades) - Recomendaciones personalizadas - Perfiles de usuario con historial

No funcionales: - 200M+ suscriptores, 100M DAU - Miles de títulos en el catálogo - Video en múltiples resoluciones (4K, 1080p, 720p, 480p) - Latencia de inicio de playback < 2s - Disponibilidad: 99.99% - Distribución global - Ancho de banda: ~15% del tráfico global de internet

Paso 2: Estimaciones

Streaming:
  100M DAU × 2 horas/día promedio = 200M horas de video/día
  Bitrate promedio: 5 Mbps (1080p)
  Ancho de banda pico: ~1 Tbps+

Almacenamiento:
  Un título en todos los formatos/resoluciones/idiomas ≈ 1 TB
  15,000 títulos × 1 TB = 15 PB
  Nuevos títulos: ~100/semana × 1 TB = 100 TB/semana

Catálogo y metadata:
  15,000 títulos × 10 KB metadata = 150 MB (cabe en memoria)

Paso 3: Diseño de Alto Nivel

graph TD
    subgraph "Clientes"
        TV[Smart TV]
        PHONE[Mobile]
        WEB[Browser]
    end

    subgraph "Content Delivery"
        TV --> CDN[CDN / Open Connect<br/>Appliances en ISPs]
        PHONE --> CDN
        WEB --> CDN
    end

    subgraph "Control Plane"
        TV --> API[API Gateway / Zuul]
        API --> CS[Catalog Service]
        API --> RS[Recommendation Service]
        API --> US[User Service]
        API --> PS[Playback Service]
    end

    subgraph "Content Pipeline"
        UPLOAD[Content Upload] --> TRANSCODE[Transcoding Pipeline<br/>Parallel encoding]
        TRANSCODE --> QC[Quality Check]
        QC --> DIST[Distribution<br/>Push to CDN]
    end

    subgraph "Data Stores"
        CS --> CSDB[(Catalog DB<br/>MySQL + Cache)]
        RS --> ML[(ML Models<br/>Feature Store)]
        RS --> HIST[(Viewing History<br/>Cassandra)]
        US --> UDB[(User DB<br/>MySQL)]
        PS --> SESS[(Session Store<br/>Redis)]
    end

    CDN --> OBJ[(Object Storage<br/>S3)]

Paso 4: Diseño Detallado

Video Transcoding Pipeline

Un video original debe convertirse a múltiples formatos y resoluciones:

graph LR
    ORIG[Video Original<br/>4K ProRes<br/>~500 GB] --> SPLIT[Splitter<br/>Dividir en segmentos<br/>de 5-10 seg]

    SPLIT --> T1[Worker 1<br/>Encode 4K H.265]
    SPLIT --> T2[Worker 2<br/>Encode 1080p H.264]
    SPLIT --> T3[Worker 3<br/>Encode 720p H.264]
    SPLIT --> T4[Worker 4<br/>Encode 480p H.264]
    SPLIT --> T5[Worker 5<br/>Audio tracks<br/>AAC / Dolby]

    T1 --> ASSEMBLE[Assembler]
    T2 --> ASSEMBLE
    T3 --> ASSEMBLE
    T4 --> ASSEMBLE
    T5 --> ASSEMBLE

    ASSEMBLE --> PACKAGE[Packager<br/>DASH / HLS<br/>Manifest files]
    PACKAGE --> S3[S3 Storage]
    S3 --> CDN_PUSH[Push to CDN<br/>Worldwide]

Datos clave: - Netflix codifica cada título en ~1200 versiones diferentes (resolución × codec × bitrate × audio) - Usan encoding por escenas: escenas de acción necesitan más bitrate que diálogos - El proceso es masivamente paralelo: cada segmento de 5-10 segundos se codifica independientemente

Adaptive Bitrate Streaming (ABR)

El cliente ajusta la calidad del video según las condiciones de red en tiempo real.

sequenceDiagram
    participant Client
    participant CDN

    Client->>CDN: GET /manifest.mpd (DASH)
    CDN-->>Client: Lista de segmentos disponibles por calidad

    Note over Client: Ancho de banda: 10 Mbps
    Client->>CDN: GET /segment_1_4K.mp4
    CDN-->>Client: Segmento 4K (8 Mbps)

    Note over Client: Red se degrada: 3 Mbps
    Client->>CDN: GET /segment_2_720p.mp4
    CDN-->>Client: Segmento 720p (2.5 Mbps)

    Note over Client: Red se recupera: 8 Mbps
    Client->>CDN: GET /segment_3_1080p.mp4
    CDN-->>Client: Segmento 1080p (5 Mbps)

Protocolo DASH (Dynamic Adaptive Streaming over HTTP):

<!-- Manifest simplificado (MPD) -->
<AdaptationSet>
  <Representation bandwidth="8000000" width="3840" height="2160">
    <SegmentList>
      <SegmentURL media="4k/segment_1.mp4"/>
      <SegmentURL media="4k/segment_2.mp4"/>
    </SegmentList>
  </Representation>
  <Representation bandwidth="5000000" width="1920" height="1080">
    <SegmentList>
      <SegmentURL media="1080p/segment_1.mp4"/>
      <SegmentURL media="1080p/segment_2.mp4"/>
    </SegmentList>
  </Representation>
</AdaptationSet>

Open Connect: El CDN Propio de Netflix

Netflix no depende de CDNs de terceros. Tiene su propia red: Open Connect.

graph TD
    subgraph "Netflix Backend (AWS)"
        CTRL[Control Plane<br/>API, Auth, Billing]
        ORIGIN[Origin<br/>S3 + Encoding]
    end

    subgraph "Open Connect Network"
        ORIGIN -->|Pre-position content<br/>Off-peak hours| OCA1[OCA en ISP 1<br/>Telcel México]
        ORIGIN --> OCA2[OCA en ISP 2<br/>Comcast USA]
        ORIGIN --> OCA3[OCA en IXP<br/>Amsterdam]
    end

    subgraph "Usuarios"
        U1[Usuario México] -->|Video stream| OCA1
        U2[Usuario USA] -->|Video stream| OCA2
        U1 -->|API calls| CTRL
    end

OCA (Open Connect Appliance): - Servidores físicos instalados directamente en ISPs y IXPs - 100-200 TB de almacenamiento SSD cada uno - Contenido pre-posicionado durante horas de bajo tráfico - ~90% del contenido se sirve desde OCAs locales (1 hop de red) - Netflix da los servidores gratis a los ISPs (ambos se benefician)

Sistema de Recomendaciones (Simplificado)

graph TD
    subgraph "Datos de Entrada"
        VH[Viewing History]
        RATINGS[Ratings / Thumbs]
        BROWSE[Browsing Behavior]
        META[Content Metadata<br/>Género, actores, tags]
        DEMO[Demographics]
    end

    subgraph "Pipeline ML"
        VH --> FE[Feature Engineering<br/>Spark]
        RATINGS --> FE
        BROWSE --> FE
        META --> FE
        DEMO --> FE

        FE --> CF[Collaborative Filtering<br/>Usuarios similares]
        FE --> CB[Content-Based<br/>Contenido similar]

        CF --> ENSEMBLE[Ensemble Model<br/>Ranking final]
        CB --> ENSEMBLE
    end

    subgraph "Serving"
        ENSEMBLE -->|Offline: batch scoring| PRECOMP[(Precomputed Recs<br/>Cassandra)]
        ENSEMBLE -->|Online: real-time| ONLINE[Online Model<br/>Feature Store + Redis]

        PRECOMP --> ROW[Rows de Homepage<br/>Para cada usuario]
        ONLINE --> ROW
    end

Datos interesantes: - Netflix estima que su sistema de recomendaciones vale $1B/año en retención de usuarios - ~80% del contenido consumido viene de recomendaciones, no de búsqueda - El artwork (thumbnail) de cada título es personalizado por usuario

Resiliencia: Chaos Engineering

Netflix es pionera en Chaos Engineering con herramientas como:

Herramienta Qué hace
Chaos Monkey Mata instancias EC2 al azar en producción
Chaos Kong Simula la caída de una región AWS completa
Latency Monkey Inyecta latencia artificial en llamadas entre servicios
FIT (Failure Injection Testing) Fuerza fallos en servicios específicos

Esto garantiza que cada servicio esté diseñado para sobrevivir fallos inesperados.


Otros Sistemas para Practicar

Una vez domines Twitter y Netflix, practica diseñando:

Sistema Conceptos Clave
URL Shortener (TinyURL) Hashing, base62, read-heavy, cache
Chat (WhatsApp) WebSockets, presence, message delivery, E2E encryption
File Storage (Dropbox) Chunking, dedup, sync, conflict resolution
Search Engine (Google) Web crawling, inverted index, PageRank, distributed computing
Ride Sharing (Uber) Geospatial index, matching, real-time tracking, ETA
Payment System (Stripe) Idempotency, exactly-once, ledger, PCI compliance
Notification System Multi-channel, rate limiting, priority queues, templates
Rate Limiter Token bucket, sliding window, distributed rate limiting

Recursos Recomendados

  • Libro: System Design Interview Vol. 1 y 2 - Alex Xu (ByteByteGo)
  • Libro: Designing Data-Intensive Applications - Martin Kleppmann
  • Blog: Netflix Tech Blog (netflixtechblog.com)
  • Blog: Twitter Engineering Blog
  • Paper: "Scaling Memcache at Facebook" - Nishtala et al.
  • Paper: "TAO: Facebook's Distributed Data Store for the Social Graph"
  • Video: Gaurav Sen - System Design series (YouTube)
  • Video: ByteByteGo - System Design Interview series (YouTube)
  • Práctica: interviewing.io - Mock interviews
  • Práctica: designgurus.org - Grokking System Design