TechLead
Leccion 2 de 30
8 min de lectura
Diseño de Sistemas

Fundamentos de Escalabilidad

Domina los fundamentos de escalabilidad incluyendo escalado vertical vs horizontal, servicios sin estado, escalado de bases de datos y estrategias de escalado del mundo real

¿Qué Es la Escalabilidad?

La escalabilidad es la capacidad de un sistema para manejar una cantidad creciente de trabajo, o su potencial para ampliarse y acomodar ese crecimiento. Un sistema escalable puede mantener o mejorar sus características de rendimiento a medida que aumenta la carga de trabajo — ya sea más usuarios, más datos o más transacciones.

La escalabilidad no se trata de qué tan rápido es un sistema hoy. Un sistema que maneja 100 solicitudes por segundo con 10ms de latencia pero no puede manejar 1,000 solicitudes por segundo sin caerse no es escalable. Por el contrario, un sistema que maneja 100 solicitudes por segundo con 50ms de latencia pero puede crecer suavemente para manejar 100,000 solicitudes por segundo es altamente escalable.

Escalado Vertical vs Escalado Horizontal

Hay dos enfoques fundamentales para escalar un sistema, y entender cuándo usar cada uno es crítico para diseñar arquitecturas efectivas.

Escalado Vertical (Scale Up)

El escalado vertical significa aumentar los recursos de una sola máquina — agregar más núcleos de CPU, más RAM, almacenamiento más rápido, o actualizar a un tipo de instancia más potente. Esta es la forma más simple de escalado porque no requiere cambios en el código de tu aplicación.

  • Pros: Simple de implementar, no requiere cambios de código, sin complejidad de sistemas distribuidos, la consistencia de datos es directa.
  • Contras: Límites estrictos de hardware (no puedes comprar una máquina con 10 millones de núcleos), punto único de fallo, costoso en el extremo alto, requiere tiempo de inactividad para actualizaciones en muchos casos.

Escalado Horizontal (Scale Out)

El escalado horizontal significa agregar más máquinas a tu conjunto de recursos. En lugar de hacer una máquina más potente, distribuyes la carga entre muchas máquinas. Este es el enfoque utilizado por virtualmente todos los sistemas a gran escala.

  • Pros: Escalabilidad casi infinita, tolerancia a fallos a través de redundancia, rentable con hardware commodity, sin punto único de fallo.
  • Contras: La aplicación debe estar diseñada para distribución, la consistencia de datos es compleja, sobrecarga de comunicación de red, más difícil de depurar y monitorear.

Comparación Escalado Vertical vs Horizontal

Aspecto Escalado Vertical Escalado Horizontal
ComplejidadBajaAlta
Curva de costosExponencialLineal
Tolerancia a fallosNinguna (SPOF)Incorporada
Límite superiorTecho de hardwareVirtualmente ilimitado
Tiempo de inactividad para actualizaciónA menudo requeridoActualizaciones progresivas posibles
Consistencia de datosSimpleCompleja (distribuida)

Servicios Sin Estado vs Con Estado

La distinción entre servicios sin estado y con estado es una de las decisiones arquitectónicas más importantes que tomarás, e impacta directamente en qué tan fácilmente puede escalar horizontalmente tu sistema.

Servicios Sin Estado

Un servicio sin estado no almacena ningún dato específico del cliente entre solicitudes. Cada solicitud contiene toda la información que el servidor necesita para procesarla. Esto significa que cualquier servidor del grupo puede manejar cualquier solicitud, haciendo el escalado horizontal trivial — simplemente agregas más servidores detrás de un balanceador de carga.

// Ejemplo de servidor API sin estado
// Cualquier instancia puede manejar cualquier solicitud
import express from 'express';

const app = express();

app.get('/api/users/:id', async (req, res) => {
  // Todo el estado está en la base de datos, no en memoria
  const user = await database.query('SELECT * FROM users WHERE id = $1', [req.params.id]);

  // La info de autenticación viene del token JWT en la solicitud
  const token = req.headers.authorization;
  const claims = verifyJWT(token);

  res.json({ user, requestedBy: claims.userId });
});

// Este servidor puede replicarse a 100 instancias
// sin ninguna coordinación entre ellas

Servicios Con Estado

Un servicio con estado almacena datos sobre cada sesión del cliente en memoria en el servidor específico que maneja ese cliente. Esto significa que las solicitudes posteriores del mismo cliente deben enrutarse al mismo servidor (conocido como "sesiones pegajosas"), lo que complica el balanceo de carga y limita la escalabilidad.

Convertir Servicios Con Estado en Sin Estado

La solución es externalizar el estado de la sesión a un almacén de datos compartido como Redis. De esta manera, cualquier servidor puede buscar la sesión, y los servidores de aplicación en sí permanecen sin estado.

  • Mover sesiones a Redis: Almacenar datos de sesión en un clúster de Redis en lugar de mapas en memoria.
  • Usar JWTs: Codificar datos de sesión directamente en el token para que no se necesite almacenamiento de sesión del lado del servidor.
  • Usar una base de datos compartida: Almacenar estado en una base de datos accesible por todos los servidores.

Estrategias de Escalado

Los sistemas del mundo real usan múltiples estrategias de escalado en combinación. Aquí están las más importantes:

1. Balanceo de Carga

Distribuir el tráfico entrante entre múltiples servidores para asegurar que ningún servidor individual se convierta en un cuello de botella. Los balanceadores de carga pueden trabajar en Capa 4 (TCP) o Capa 7 (HTTP) y usan varios algoritmos para decidir qué servidor recibe cada solicitud.

2. Caché

Almacenar datos frecuentemente accedidos en un almacén rápido en memoria (como Redis o Memcached) para reducir la carga en las bases de datos y acelerar las respuestas. El caché puede reducir las consultas a la base de datos en un 80-90% en cargas de trabajo con muchas lecturas.

3. Replicación de Base de Datos

Crear réplicas de lectura de tu base de datos para distribuir las consultas de lectura. En la mayoría de las aplicaciones, las lecturas superan a las escrituras en una proporción de 10:1 o incluso 100:1, por lo que descargar las lecturas a las réplicas aumenta dramáticamente el rendimiento.

// Ejemplo de enrutamiento de réplicas de lectura
class DatabaseRouter {
  private primary: DatabaseConnection;
  private replicas: DatabaseConnection[];
  private currentReplica = 0;

  async query(sql: string, params: any[], isWrite: boolean) {
    if (isWrite) {
      // Las escrituras siempre van al primario
      return this.primary.query(sql, params);
    }

    // Las lecturas se distribuyen entre las réplicas (round-robin)
    const replica = this.replicas[this.currentReplica];
    this.currentReplica = (this.currentReplica + 1) % this.replicas.length;
    return replica.query(sql, params);
  }
}

4. Particionamiento de Base de Datos (Sharding)

Particionar datos entre múltiples instancias de base de datos basándose en una clave de partición (como ID de usuario o región geográfica). Cada partición contiene un subconjunto de los datos, permitiendo que el sistema escale el rendimiento de escritura de forma lineal.

5. Procesamiento Asíncrono

Mover tareas que consumen tiempo (enviar correos electrónicos, generar informes, procesar imágenes) fuera del ciclo solicitud-respuesta hacia trabajos en segundo plano usando colas de mensajes.

6. Redes de Distribución de Contenido (CDNs)

Cachear recursos estáticos (imágenes, CSS, JavaScript, videos) en ubicaciones edge alrededor del mundo. Esto reduce la latencia para usuarios globales y descarga tráfico de tus servidores de origen.

Enfoques de Escalado de Base de Datos

La base de datos es casi siempre el primer cuello de botella en un sistema en crecimiento. Aquí están los pasos progresivos para escalar bases de datos:

  1. Optimizar consultas: Agregar índices adecuados, reescribir consultas lentas, usar planes EXPLAIN. Esto es gratis y siempre debe hacerse primero.
  2. Agregar caché: Cachear resultados frecuentes de consultas en Redis para reducir la carga de la base de datos.
  3. Escalado vertical: Actualizar a una instancia de base de datos más grande con más CPU y RAM.
  4. Réplicas de lectura: Crear réplicas de lectura y enrutar consultas de lectura a ellas.
  5. Pool de conexiones: Usar un administrador de conexiones como PgBouncer para gestionar conexiones de base de datos eficientemente.
  6. Sharding: Particionar datos entre múltiples instancias de base de datos.
  7. Considerar NoSQL: Para patrones de acceso específicos, una base de datos NoSQL como DynamoDB o Cassandra puede escalar más naturalmente.

Ejemplos de Escalado del Mundo Real

  • Instagram: Comenzó con un solo servidor Django y base de datos PostgreSQL. Escaló a miles de millones de usuarios usando sharding de PostgreSQL, Memcached, Cassandra para almacenamiento de feed y un CDN personalizado. Mantuvieron Python/Django a través de todo esto.
  • Twitter: Originalmente una app monolítica de Ruby on Rails. Se descompuso en microservicios, movió la línea de tiempo de MySQL a un almacén en memoria personalizado (Manhattan), y usa Kafka para procesamiento de eventos en tiempo real.
  • Netflix: Migró de una app monolítica Java en un centro de datos a cientos de microservicios en AWS. Usa Cassandra para almacenamiento distribuido, EVCache (basado en Memcached) para caché, y sirve el 90% del tráfico a través de su CDN Open Connect.

Puntos Clave

  • Siempre empieza simple y escala incrementalmente según sea necesario. La optimización prematura es la raíz de mucha complejidad innecesaria.
  • Haz tus servicios sin estado siempre que sea posible — este es el mayor habilitador del escalado horizontal.
  • La base de datos generalmente es el primer cuello de botella. Ten un plan claro de cómo la escalarás.
  • El caché y los CDNs proporcionan el mayor beneficio por el esfuerzo en la mayoría de las aplicaciones.
  • Los sistemas del mundo real usan una combinación de todas estas estrategias, no solo una.

Continuar Aprendiendo