Escalado de bases de datos: evitar el cuello de botella
La base de datos es siempre el único punto de falla en el escalado. Estrategias de optimización de Postgres, réplicas de lectura y fragmentación.
El eslabón más débil del monolito
Puede escalar sus servidores Frontend infinitamente (solo agrega más nodos detrás del Load Balancer). Puede escalar sus servidores API infinitamente (funciones sin servidor). No puedes escalar fácilmente tu base de datos principal. (Por lo general) solo hay una fuente de verdad. Un disco. Un proceso maestro. Cuando el tráfico aumenta, la CPU alcanza el 100% y la latencia de las consultas pasa de 10 ms a 10 000 ms. El sitio muere. Escalar la base de datos es el problema más difícil en la ingeniería backend.
Por qué Maison Code analiza esto
En Maison Code, vemos que startups válidas mueren debido a SQL.
Escriben código como SELECT * FROM Orders.
Funciona bien con 100 pedidos.
Se bloquea el servidor con 1.000.000 de pedidos.
Optimizamos las bases de datos para Alta Cardinalidad y Alto Rendimiento.
Sabemos que “Agregar índices” no es suficiente. Necesita cambios de arquitectura (almacenamiento en caché, replicación, particionamiento).
Estrategia 1: Indexación (la fruta madura)
A la mayoría de los problemas de rendimiento les faltan índices.
Escenario: Búsqueda de un usuario por correo electrónico.
Consulta: SELECCIONAR * DE usuarios DONDE correo electrónico = 'alex@example.com'.
Sin Índice: La BD escanea 1.000.000 de filas (Escaneo Secuencial). EN).
Con índice (B-Tree): La base de datos salta directamente al correo electrónico. O(logN).
Costo: Los índices ralentizan las escrituras (INSERT/UPDATE), porque el árbol de índice debe actualizarse.
Regla: columnas de índice utilizadas en WHERE, JOIN y ORDER BY.
Estrategia 2: almacenamiento en caché (Redis)
La consulta más rápida es la que no haces. SQL es lento (E/S de disco, matemáticas de CPU). Redis es rápido (en memoria, valor clave). Patrón: caché de búsqueda.
función asíncrona getProduct(id) {
// 1. Verificar caché
const en caché = await redis.get(`producto:${id}`);
si (en caché) devuelve JSON.parse (en caché);
// 2. Consultar base de datos (lento)
producto const = await db.query('SELECCIONAR * DE productos DONDE id =?', [id]);
// 3. Guardar en caché (TTL 5 minutos)
await redis.set(`producto:${id}`, JSON.stringify(producto), 'EX', 300);
devolver producto;
}
Esto descarga el 90% del tráfico de lectura de la base de datos.
Estrategia 3: leer réplicas
Incluso con el almacenamiento en caché, las lecturas pueden saturar la CPU. Solución: Crear copias de la base de datos (Réplicas).
- Nodo principal: maneja escrituras (INSERTAR, ACTUALIZAR, ELIMINAR). Sincroniza los cambios con réplicas (Async).
- Nodos de réplica: Manejar lecturas (SELECCIONAR). Compensación: Retraso de replicación. El usuario actualiza su perfil. Actualizan la página inmediatamente. Llegaron a una réplica que aún no ha recibido la actualización. Ven el perfil antiguo. Solución: “Lee tus propios escritos”. Para el usuario actual, fuerce la lectura desde Primario. Para datos públicos, lea desde Réplica.
Estrategia 4: agrupación de conexiones (PgBouncer)
Postgres tiene un límite de conexiones simultáneas (por ejemplo, mantener 100). Las funciones sin servidor (Lambda) generan 1000 instancias. Si 1000 instancias intentan abrir una conexión, Postgres falla. Solución: Un proxy (PgBouncer / Supabase Pooler). El Proxy tiene 100 conexiones abiertas a la base de datos. Las 1.000 Lambdas hablan con el Proxy. El Proxy pone en cola las consultas y las ejecuta utilizando las 100 conexiones. Esto es obligatorio para arquitecturas sin servidor.
Estrategia 5: Partición vertical (tablas divididas)
La tabla Usuarios tiene bio (texto, enorme) y last_login (fecha, pequeño).
Si consulta con frecuencia last_login, pero bio hace que el tamaño de la fila sea enorme, la base de datos lee demasiados datos del disco.
Divídelo:
Tabla User_Core (id, correo electrónico, contraseña, last_login).
Tabla User_Profile (id, biografía, avatar).
Ahora SELECT last_login FROM User_Core es increíblemente rápido porque caben más filas en las páginas RAM.
6. Vistas materializadas: cálculo previo del éxito
“Muéstrame los ingresos totales para 2024”.
SELECCIONE SUMA (monto) DE pedidos DONDE año = 2024.
Esto escanea 10 millones de filas. Tarda 5 segundos.
Solución: Vista materializada.
CREAR VISTA MATERIALIZADA ingresos_anuales COMO SELECCIONAR...
Postgres calcula el resultado y lo guarda en el disco como una tabla física.
Consultarlo tarda 1 ms.
Compensación: Los datos están obsoletos. Debe “ACTUALIZAR LA VISTA MATERIALIZADA” periódicamente (por ejemplo, cada hora).
Perfecto para paneles donde no se requiere estrictamente “tiempo real”.
7. Escalado automático de bases de datos (Aurora sin servidor)
¿Qué pasa si no desea administrar réplicas? Amazon Aurora sin servidor v2. Agrega automáticamente CPU/RAM cuando el tráfico aumenta y se reduce cuando el tráfico disminuye. Se escala en incrementos fraccionarios (ACU). Efectivamente le brinda la promesa de “Escalamiento Infinito” de NoSQL, pero con total compatibilidad con SQL. Es caro, pero más barato que contratar un DBA para aprovisionar manualmente réplicas de lectura a las 3 a.m.
9. Datos de series de tiempo (TimescaleDB)
“Registra cada vista de página”.
“Registre cada lectura del termómetro”.
Postgres estándar se ahoga al insertar 10.000 filas/segundo en una sola tabla.
Los índices se fragmentan.
Solución: TimescaleDB (extensión Postgres).
Particiona automáticamente los datos por tiempo (“Hypertables”).
pedidos_2024_01, pedidos_2024_02.
Lo consulta como una tabla “órdenes”, pero físicamente son fragmentos pequeños.
Eliminar datos antiguos es instantáneo (DROP TABLEorders_2020). ELIMINAR DE pedidos DONDE año = 2020 lleva horas.
10. Estrategias avanzadas de partición
La partición vertical divide las columnas.
Partición horizontal divide filas.
Por Región: users_eu, users_us.
Esto ayuda con GDPR (Residencia de datos).
“Los datos europeos nunca salen del servidor de la UE”.
La partición declarativa de Postgres hace que esto sea manejable.
Así es como escalamos aplicaciones SaaS a millones de inquilinos sin comprar un Mainframe.
11. La visión del escéptico
“Simplemente use DynamoDB/NoSQL. Se escala infinitamente”. Contrapunto: NoSQL escala las escrituras, pero falla en Datos relacionales. “Muéstrame todos los pedidos de usuarios en París que compraron zapatos rojos”. En SQL: una consulta. En NoSQL: una pesadilla de uniones del lado de la aplicación y múltiples recuperaciones. La mayoría de los datos del comercio electrónico son relacionales. Cíñete a SQL (Postgres) hasta llegar a la escala de Google.
Preguntas frecuentes
P: ¿Cuándo utilizar Sharding? R: Casi nunca. La fragmentación (división de datos en varios servidores según la ID del usuario) es extremadamente compleja. Pierde transacciones ACID entre fragmentos. No fragmente hasta que tenga > 10 TB de datos. El escalado vertical (servidor más grande) funciona sorprendentemente bien hasta ese punto.
P: ¿ORM frente a SQL sin formato? R: Prisma/Drizzle (ORM) para productividad. SQL sin formato para informes complejos o consultas súper optimizadas. Los ORM modernos son lo suficientemente buenos para el 99% de las consultas.
Conclusión
La base de datos es el corazón de su pila. Si deja de latir, la aplicación muere. Trátelo con respeto. Indexe sus claves externas. Guarde en caché sus rutas activas. Y nunca, jamás, ejecute “DROP TABLE” el viernes.
¿Base de datos asfixiada?
Si sus consultas se están agotando o su CPU está en rojo, Maison Code puede optimizar su esquema. Analizamos planes de consulta, implementamos estrategias de almacenamiento en caché y diseñamos conjuntos de réplicas.
¿Las consultas son demasiado lentas?
Optimizamos la arquitectura de la base de datos mediante indexación, almacenamiento en caché y réplicas de lectura para manejar una escala masiva. Contrata a nuestros Arquitectos.