Implementación de Optimizaciones Avanzadas

Video
30 min~4 min lectura

Reproductor de video

Concepto clave

En sistemas de reservas de alta demanda, la concurrencia es el principal desafío. Imagina un concierto donde miles de personas intentan comprar boletos simultáneamente: sin optimizaciones, el sistema colapsa. PostgreSQL ofrece mecanismos como transacciones aisladas, bloqueos optimistas y particionamiento para manejar esta carga.

La clave está en minimizar el tiempo de bloqueo de recursos. En lugar de bloquear filas por largos periodos (como en transacciones pesadas), usamos técnicas que permiten múltiples lecturas y escrituras controladas. Esto es similar a un sistema de turnos en un banco: en lugar de hacer una fila única, tienes múltiples cajeros con reglas claras para evitar conflictos.

Cómo funciona en la práctica

Vamos a implementar un sistema de reservas para 10,000 asientos en un estadio. El enfoque tradicional usaría SELECT FOR UPDATE para bloquear asientos, pero esto crea cuellos de botella. En su lugar, usaremos optimistic concurrency control con versionado.

Paso 1: Diseñamos la tabla con una columna de versión. Paso 2: Cuando un usuario intenta reservar, verificamos si la versión no ha cambiado desde que leyó los datos. Paso 3: Si hay conflicto (otro usuario reservó primero), reintentamos o informamos al usuario. Esto reduce bloqueos y mejora el throughput.

Codigo en accion

Primero, el diseño inicial ineficiente:

-- Tabla básica (problema: bloqueos pesados)
CREATE TABLE reservas (
    id SERIAL PRIMARY KEY,
    asiento_id INT NOT NULL,
    usuario_id INT NOT NULL,
    reservado BOOLEAN DEFAULT FALSE,
    creado_en TIMESTAMP DEFAULT NOW()
);

-- Reserva tradicional con bloqueo
BEGIN;
SELECT * FROM reservas WHERE asiento_id = 100 AND reservado = FALSE FOR UPDATE;
-- Lógica de negocio aquí
UPDATE reservas SET reservado = TRUE, usuario_id = 123 WHERE asiento_id = 100;
COMMIT;

Ahora, la versión optimizada:

-- Tabla optimizada con versionado
CREATE TABLE reservas_optimizadas (
    id SERIAL PRIMARY KEY,
    asiento_id INT NOT NULL UNIQUE,
    usuario_id INT,
    reservado BOOLEAN DEFAULT FALSE,
    version INT DEFAULT 0,
    creado_en TIMESTAMP DEFAULT NOW()
);

-- Reserva con control optimista
UPDATE reservas_optimizadas 
SET reservado = TRUE, usuario_id = 123, version = version + 1
WHERE asiento_id = 100 AND reservado = FALSE AND version = 0;
-- Verificar filas afectadas: si es 0, alguien más reservó primero

Errores comunes

  • Usar bloqueos pesados en tablas grandes: Evita SELECT FOR UPDATE sin límites; usa WHERE específico o versionado.
  • Ignorar índices para columnas de concurrencia: Asegúrate de indexar columnas como asiento_id y version para búsquedas rápidas.
  • No manejar reintentos: Implementa lógica de retry con backoff exponencial para conflictos.
  • Sobrecargar una sola instancia: Considera replication con read replicas para distribuir carga de lecturas.
  • Olvidar monitoreo: Usa herramientas como pg_stat_activity para detectar bloqueos en tiempo real.

Checklist de dominio

  1. ¿Implementé versionado en transacciones para reducir bloqueos?
  2. ¿Los índices cubren todas las queries de alta frecuencia?
  3. ¿Tengo un plan de reintentos para conflictos de concurrencia?
  4. ¿Uso particionamiento para tablas con millones de filas?
  5. ¿Configuré parámetros como max_connections y work_mem adecuadamente?
  6. ¿Probé la carga con herramientas como pgbench?
  7. ¿Documenté las estrategias de optimización para el equipo?

Optimizar un sistema de reservas con 100k usuarios simultáneos

En este ejercicio, mejorarás una tabla de reservas existente para manejar alta concurrencia. Sigue estos pasos:

  1. Crea una base de datos llamada reservas_optimizadas en tu instancia PostgreSQL.
  2. Ejecuta el código inicial proporcionado en la lección para crear la tabla básica y la optimizada.
  3. Simula alta concurrencia insertando 50,000 asientos con un script (usa un loop en psql o un lenguaje de programación).
  4. Implementa un script que simule 100 usuarios intentando reservar el mismo asiento (asiento_id = 500) usando la versión optimizada.
  5. Mide el tiempo total y los conflictos, comparando con el enfoque tradicional.
  6. Agrega un índice compuesto en (asiento_id, version) y vuelve a medir el rendimiento.
  7. Documenta tus hallazgos en un README.md con métricas clave.
Pistas
  • Usa pgbench o scripts en Python con psycopg2 para simular concurrencia.
  • Monitorea los bloqueos con SELECT * FROM pg_locks durante la prueba.
  • Ajusta el parámetro max_connections si necesitas más conexiones simultáneas.

Evalua tu comprension

Completa el quiz interactivo de arriba para ganar XP.