Un sistema puede funcionar perfectamente en tu portátil con un único usuario y, sin embargo, derrumbarse el día del lanzamiento cuando llegan miles de personas a la vez. El rendimiento no se adivina: se mide. Y la única forma de saber cómo se comportará un sistema bajo carga real antes de que ocurra es someterlo a pruebas de carga controladas. En esta lección aprenderás a distinguir y medir correctamente las dos magnitudes fundamentales del rendimiento (latencia y throughput), por qué los promedios mienten y debes usar percentiles (p95, p99), los distintos tipos de prueba según lo que quieras descubrir (carga, estrés, soak, spike), cómo escribir una prueba real con k6 y JMeter, y cómo localizar los cuellos de botella que limitan tu sistema.
Contenido
- Latencia vs Throughput
- Por qué los promedios mienten: percentiles p95/p99
- Tipos de prueba de rendimiento
- Ejemplo práctico con k6 y JMeter
- Identificación de cuellos de botella
- Errores comunes y consejos
- Ejercicios
- Latencia vs Throughput
Son las dos métricas básicas del rendimiento y conviene no confundirlas:
- Latencia: el tiempo que tarda una petición en completarse. Se mide en milisegundos. Responde a "¿cómo de rápido?".
- Throughput (rendimiento/caudal): el número de peticiones que el sistema procesa por unidad de tiempo. Se mide en peticiones por segundo (req/s). Responde a "¿cuántas a la vez?".
| Latencia | Throughput | |
|---|---|---|
| Mide | Tiempo de una petición | Peticiones por segundo |
| Unidad | ms (milisegundos) | req/s (peticiones/seg) |
| Pregunta | ¿Cómo de rápido responde? | ¿Cuánto volumen aguanta? |
| Lo nota | El usuario individual | El sistema en conjunto |
La analogía de la autopista lo aclara: la latencia es lo que tarda un coche en recorrerla; el throughput es cuántos coches pasan por minuto. Y son independientes: una autopista puede tener mucho throughput (muchos carriles) pero alta latencia (es muy larga). De hecho, cuando un sistema se satura, el throughput se estanca mientras la latencia se dispara, porque las peticiones empiezan a hacer cola.
- Por qué los promedios mienten: percentiles p95/p99
Imagina que mides la latencia y obtienes una media de 100 ms. Suena bien... pero la media oculta los casos malos. Si de cada 100 peticiones, 95 tardan 50 ms y 5 tardan 1.500 ms, la media sigue siendo baja, pero 1 de cada 20 usuarios sufre una experiencia pésima.
La solución son los percentiles:
| Percentil | Significado |
|---|---|
| p50 (mediana) | La mitad de las peticiones tardan menos que esto |
| p95 | El 95% tarda menos; el 5% peor queda fuera |
| p99 | El 99% tarda menos; refleja el 1% peor |
| p99,9 | Para servicios muy exigentes; la "cola larga" |
Latencias de 20 peticiones (ms), ordenadas: 40 42 45 48 50 50 52 55 58 60 62 65 70 75 80 90 110 250 800 1500 Media (promedio): ~135 ms <- engañosa, la "infla" un único valor de 1500 p50 (mediana): ~61 ms <- la experiencia típica p95: ~800 ms <- 1 de cada 20 sufre esto p99: ~1500 ms <- el peor caso real
La regla profesional: define tus objetivos (SLO) en percentiles, no en medias. Un buen objetivo es del tipo "el p95 debe estar por debajo de 300 ms". Los percentiles altos (p99, p99,9) importan especialmente porque en sistemas con muchas dependencias, una petición de usuario activa muchas internas, y basta con que una caiga en la cola lenta para arruinar la experiencia.
- Tipos de prueba de rendimiento
No todas las pruebas buscan lo mismo. Según la forma de aplicar la carga, distinguimos:
| Tipo | Qué hace | Qué descubre |
|---|---|---|
| Carga (load) | Carga esperada sostenida | ¿Cumplo mis SLO en condiciones normales? |
| Estrés (stress) | Subir la carga hasta romper | ¿Cuál es mi límite? ¿Cómo falla? |
| Soak (resistencia) | Carga moderada durante horas/días | ¿Hay fugas de memoria o degradación lenta? |
| Spike (pico) | Subida brusca y repentina | ¿Aguanta un pico súbito (oferta flash, viral)? |
graph LR
L["Load: carga estable"]
S["Stress: sube hasta romper"]
K["Soak: estable, muchas horas"]
P["Spike: pico repentino"]Cada tipo responde a un riesgo distinto del negocio:
- La prueba de carga valida que el sistema cumple los SLO con el tráfico previsto.
- La de estrés revela el punto de ruptura y, crucialmente, cómo se rompe (¿degrada con elegancia o colapsa?).
- La de soak detecta problemas que solo aparecen con el tiempo: fugas de memoria, conexiones que no se cierran, discos que se llenan de logs.
- La de spike simula eventos como una campaña viral o una venta flash, comprobando si el autoescalado reacciona a tiempo.
- Ejemplo práctico con k6 y JMeter
k6 es una herramienta moderna de pruebas de carga en la que los escenarios se escriben como código JavaScript, ideal para integrarla en CI/CD.
import http from 'k6/http';
import { check, sleep } from 'k6';
// Definimos el perfil de carga: subimos, mantenemos y bajamos usuarios virtuales
export const options = {
stages: [
{ duration: '30s', target: 50 }, // Sube de 0 a 50 usuarios en 30s
{ duration: '1m', target: 50 }, // Mantiene 50 usuarios durante 1 minuto
{ duration: '20s', target: 0 }, // Baja a 0 (rampa de bajada)
],
thresholds: {
http_req_duration: ['p(95)<300'], // CRITERIO: el p95 debe ser < 300 ms
http_req_failed: ['rate<0.01'], // y menos del 1% de errores
},
};
export default function () {
const res = http.get('https://api.miempresa.com/productos');
check(res, { 'status 200': (r) => r.status === 200 }); // Verifica respuesta OK
sleep(1); // Pausa de 1s simulando el "tiempo de lectura" del usuario
}Explicación detallada: el bloque stages define un perfil de carga en tres fases (rampa de subida, meseta y bajada), donde target son los usuarios virtuales (VU) simultáneos. Los thresholds son el corazón de la prueba: son los criterios de aprobado/suspenso; si el p95 supera 300 ms o los errores pasan del 1%, k6 marca la prueba como fallida (muy útil para que un pipeline de CI/CD bloquee un despliegue lento). La función default es lo que ejecuta cada usuario virtual en bucle: pide la lista de productos, verifica con check que devuelve 200, y hace una pausa de 1 segundo simulando comportamiento humano.
JMeter, de Apache, es la herramienta clásica, basada en interfaz gráfica y planes de prueba en XML. Es muy potente y madura, con soporte para muchos protocolos. Comparativa rápida:
| k6 | JMeter | |
|---|---|---|
| Definición de la prueba | Código (JavaScript) | GUI / XML |
| Curva de aprendizaje | Suave para desarrolladores | Más pronunciada |
| Integración CI/CD | Excelente (nativa) | Posible, más laboriosa |
| Consumo de recursos | Bajo y eficiente | Mayor (basado en JVM/hilos) |
| Protocolos | Sobre todo HTTP/web | Muchos (JDBC, FTP, JMS...) |
La elección depende del contexto: k6 brilla en equipos de desarrollo y automatización; JMeter en escenarios complejos y multiprotocolo o cuando se prefiere una herramienta visual.
- Identificación de cuellos de botella
Un cuello de botella (bottleneck) es el recurso que limita el rendimiento de todo el sistema: por mucho que mejores el resto, el sistema no irá más rápido que su componente más lento. La metodología consiste en aplicar carga y observar qué recurso se satura primero.
Sospechosos habituales, en orden de frecuencia:
| Cuello de botella | Síntoma típico | Posible solución |
|---|---|---|
| Base de datos | CPU de la BD al 100%, consultas lentas | Índices, caché, réplicas de lectura |
| Pool de conexiones | Peticiones esperando conexión libre | Aumentar el pool, liberar conexiones antes |
| CPU de la aplicación | CPU al 100% con poco tráfico | Optimizar código, escalar horizontalmente |
| Memoria | Garbage collection frecuente, swapping | Más RAM, corregir fugas de memoria |
| Red / E/S | Latencia alta sin saturar CPU | Comprimir, reducir llamadas, CDN |
| Bloqueos / contención | Throughput no sube al añadir usuarios | Reducir secciones críticas (recuerda la USL) |
El proceso correcto es iterativo: encuentras el cuello de botella, lo resuelves, vuelves a medir... y aparece otro cuello de botella distinto (porque ahora el límite está en otra parte). Optimizar es ir destapando límites sucesivos. Y una advertencia esencial: no optimices sin medir antes. La intuición sobre dónde está el problema suele equivocarse; los datos de una prueba de carga, no.
Errores Comunes y Consejos
- Fijarse solo en la media. Oculta los casos malos. Mide siempre p95 y p99.
- Confundir latencia con throughput. Son cosas distintas; un sistema rápido por unidad puede tener poco caudal y viceversa.
- Probar contra un entorno distinto de producción. Si el entorno de pruebas tiene la mitad de recursos o datos de juguete, los resultados no son extrapolables. Usa un entorno realista.
- No incluir tiempos de espera realistas. Usuarios que machacan sin pausa (
sleep) generan una carga irreal. Modela el comportamiento humano. - Optimizar a ciegas. Adivinar el cuello de botella desperdicia esfuerzo. Mide, identifica y solo entonces actúa.
- Olvidar las pruebas soak. Muchos fallos (fugas de memoria) solo aparecen tras horas. No basta con pruebas cortas.
- Consejo: integra una prueba de carga en tu CI/CD con thresholds en percentiles, para que el rendimiento sea un criterio de calidad automático y no una sorpresa en producción.
Ejercicios
-
Interpretar percentiles. Un servicio tiene latencia media de 80 ms, p50 de 70 ms, p95 de 600 ms y p99 de 2.000 ms. ¿Está sano? ¿Qué te preocupa y qué investigarías?
-
Elegir el tipo de prueba. Para cada objetivo, indica el tipo de prueba: (a) saber a partir de cuántos usuarios el sistema deja de cumplir el SLO; (b) comprobar si hay una fuga de memoria que tumba el servicio tras 12 horas; (c) validar que el sistema sobrevive al pico de tráfico del lanzamiento de un producto.
-
Definir thresholds. El negocio exige que "el 99% de las peticiones respondan en menos de medio segundo y que fallen menos del 0,5%". Escribe el bloque
thresholdsde k6 correspondiente.
Soluciones
-
No está sano, pese a la buena media y mediana. El p50 de 70 ms indica que la experiencia típica es buena, pero el p95 de 600 ms y, sobre todo, el p99 de 2.000 ms revelan una "cola larga": el 5% de los usuarios sufre lentitud y el 1% espera 2 segundos. Investigaría qué tienen en común esas peticiones lentas: ¿consultas a la base de datos sin índice?, ¿pausas de garbage collection?, ¿llamadas a un servicio externo lento? Las trazas distribuidas (lección anterior) son la herramienta ideal para localizarlo.
-
(a) Prueba de estrés (subir la carga hasta encontrar el punto donde se incumple el SLO/se rompe). (b) Prueba de soak/resistencia (carga sostenida durante muchas horas para detectar degradación lenta). (c) Prueba de spike/pico (subida brusca y repentina de tráfico).
-
Bloque de thresholds:
thresholds: { http_req_duration: ['p(99)<500'], // p99 por debajo de 500 ms http_req_failed: ['rate<0.005'], // menos del 0,5% de errores }
Conclusión
Has aprendido que el rendimiento se mide, no se supone: que la latencia (tiempo por petición) y el throughput (volumen por segundo) son magnitudes distintas, que los percentiles p95/p99 cuentan la verdad que la media oculta, que cada tipo de prueba (carga, estrés, soak, spike) responde a un riesgo distinto, cómo escribir pruebas reales con k6 y JMeter, y cómo localizar cuellos de botella de forma iterativa y basada en datos. Validar el rendimiento antes de producción es la diferencia entre un lanzamiento exitoso y un colapso público.
Con esta lección cierras el Módulo 9, Calidad, Seguridad y Observabilidad. Has recorrido la escalabilidad, la alta disponibilidad, la seguridad por diseño, la observabilidad y el rendimiento: los cinco atributos que separan una aplicación que "funciona en mi máquina" de un sistema robusto, seguro y preparado para el mundo real. Estos conceptos no son fases finales, sino criterios que deben guiar cada decisión de arquitectura desde el primer día.
Curso de Arquitectura de Aplicaciones
Módulo 1: Fundamentos de la Arquitectura de Aplicaciones
- ¿Qué es la Arquitectura de Aplicaciones?
- El Rol del Arquitecto de Software
- Atributos de Calidad y Requisitos No Funcionales
- Decisiones Arquitectónicas y Compromisos (Trade-offs)
- Documentación de Arquitectura: Vistas y el Modelo C4
Módulo 2: Principios y Tácticas de Diseño
- Acoplamiento, Cohesión y Separación de Responsabilidades
- Principios SOLID Aplicados a la Arquitectura
- DRY, KISS, YAGNI y Otros Principios de Diseño
- Tácticas Arquitectónicas para los Atributos de Calidad
- Gestión de la Deuda Técnica
Módulo 3: Estilos y Patrones Arquitectónicos
- Arquitectura Monolítica
- Arquitectura en Capas (N-Tier)
- Arquitectura Cliente-Servidor
- Arquitectura Hexagonal (Puertos y Adaptadores)
- Arquitectura Limpia y Cebolla (Clean & Onion)
Módulo 4: Arquitecturas Distribuidas y Microservicios
- Introducción a los Sistemas Distribuidos
- Arquitectura de Microservicios
- Descomposición de Servicios y Bounded Contexts
- API Gateway, Service Discovery y Comunicación entre Servicios
- Patrones de Resiliencia: Circuit Breaker, Retry y Bulkhead
- El Teorema CAP y la Consistencia de Datos
Módulo 5: Arquitecturas Dirigidas por Eventos y Mensajería
- Fundamentos de la Arquitectura Orientada a Eventos
- Mensajería Asíncrona: Colas y Brokers
- Patrones de Eventos: Event Sourcing y CQRS
- Gestión de Transacciones Distribuidas: Patrón Saga
- Streaming de Datos en Tiempo Real
Módulo 6: Diseño Dirigido por el Dominio (DDD)
- Conceptos Fundamentales del DDD
- Diseño Estratégico: Bounded Contexts y Lenguaje Ubicuo
- Diseño Táctico: Entidades, Agregados y Repositorios
- Mapeo de Contextos (Context Mapping)
Módulo 7: Datos y Persistencia
- Estrategias de Persistencia: SQL vs NoSQL
- Patrones de Acceso a Datos: Repository, Unit of Work y DAO
- Base de Datos por Servicio y Gestión de Datos Distribuidos
- Caché y Estrategias de Invalidación
Módulo 8: Arquitectura en la Nube y Despliegue
- Fundamentos del Cloud Computing (IaaS, PaaS, SaaS)
- Contenedores y Orquestación con Docker y Kubernetes
- Arquitectura Serverless
- Patrones de Diseño Cloud-Native
- Infraestructura como Código (IaC)
Módulo 9: Calidad, Seguridad y Observabilidad
- Escalabilidad: Horizontal vs Vertical y Balanceo de Carga
- Alta Disponibilidad y Tolerancia a Fallos
- Seguridad por Diseño y Autenticación/Autorización
- Observabilidad: Logging, Métricas y Trazabilidad
- Rendimiento y Pruebas de Carga
