Software Engineering5 de marzo de 20268 min read

Patrones de escalado de bases de datos antes de hacer sharding

El sharding es costoso, permanente y difícil de revertir. Estos son los patrones de escalado que conviene agotar primero, en el orden en que recurrimos a ellos en sistemas de producción reales.

Por Innovation T Team


Se habla del sharding como si fuera un rito de iniciación, el momento en que una base de datos por fin madura. En la práctica es una de las decisiones más costosas y menos reversibles que puedes tomar, y la mayoría de los equipos recurre a él años antes de necesitarlo. Este es el orden que seguimos cuando una base de datos empieza a doler, y por qué cada peldaño te da tiempo antes de comprometerte con la opción difícil.

Por qué el sharding es el último recurso

El sharding consiste en repartir tus datos entre varias instancias de base de datos independientes, de modo que ninguna máquina contenga el conjunto completo. Parece más capacidad, y lo es. También es un impuesto sobre casi todo lo que construyas después.

Una vez que los datos viven en fragmentos (shards) separados, las uniones entre fragmentos (cross-shard joins) dejan de ser gratuitas, las transacciones que abarcan varios fragmentos necesitan coordinación a nivel de aplicación, y cada consulta tiene que saber con qué fragmento hablar. Reequilibrar un fragmento sobrecargado es un proyecto, no un cambio de configuración. Análisis que antes eran una sola consulta se convierten en una operación de dispersión y recolección (scatter-gather) entre nodos. Según nuestra experiencia, los equipos que hacen sharding pronto invierten más tiempo de ingeniería sorteando su propia topología del que jamás dedicaron al problema de rendimiento que lo desencadenó.

Por eso el objetivo de este artículo no es "cómo hacer sharding". Es "cómo no necesitarlo todavía". Cada patrón de los que siguen es más barato de adoptar y más barato de deshacer.

Mide antes de moverte

Antes de tocar la arquitectura, consigue cifras honestas. Escalar la capa equivocada es como los equipos terminan añadiendo réplicas de lectura para solucionar un problema que en realidad era un índice ausente.

Recopila como mínimo:

  • Los registros de consultas lentas con planes de ejecución reales, no suposiciones sobre qué consultas son lentas.
  • Latencia p95 y p99 por endpoint, no promedios. Los promedios ocultan las consultas que hacen que los usuarios se marchen.
  • El número de conexiones a lo largo del tiempo, incluidas las conexiones inactivas y en espera.
  • La proporción de lectura/escritura. Una carga de trabajo con un 95 % de lecturas y otra con un 60 % de lecturas exigen peldaños distintos.
  • El tamaño de tablas e índices, además de la tasa de crecimiento de filas por mes.

Si hoy no puedes ver estos datos, esa es tu primera tarea. No puedes escalar lo que no puedes medir, y la mitad de las veces los datos revelan una solución que cuesta una tarde en lugar de un trimestre.

La escalera del escalado

Este es el orden en que subimos, de lo más barato y seguro a lo más invasivo. Avanza de arriba abajo y detente en cuanto tus cifras vuelvan a estar sanas.

  1. Arregla las consultas y los índices.
  2. Dimensiona y agrupa tus conexiones adecuadamente.
  3. Añade caché donde el patrón de lectura lo permita.
  4. Descarga las lecturas a réplicas.
  5. Escala el primario verticalmente.
  6. Particiona las tablas grandes dentro de una sola base de datos.
  7. Separa por dominio (almacenes distintos o CQRS).
  8. Solo entonces, haz sharding.

1. Primero las consultas y los índices

El trabajo de mayor retorno casi nunca es arquitectónico. Es un índice ausente en una columna de WHERE o JOIN, una consulta N+1 que se dispara cientos de veces por petición, o un SELECT * que arrastra filas anchas cuando bastaría con tres columnas.

Ejecuta EXPLAIN ANALYZE (Postgres) o su equivalente en tus consultas más lentas. Busca escaneos secuenciales (sequential scans) sobre tablas grandes, ordenamientos que se desbordan al disco y bucles anidados sobre grandes cantidades de filas. Añade índices de cobertura (covering indexes) para que la base responda directamente desde el índice. Pero ten cuidado con la sobreindexación: cada índice que añades ralentiza las escrituras y consume memoria, así que indexa para las consultas que de verdad ejecutas y luego elimina las que nunca se usan.

Este peldaño recorta con regularidad la latencia p99 en un margen amplio a un coste de infraestructura casi nulo. Hazlo antes que cualquier otra cosa.

2. Pooling de conexiones y límites

Las bases de datos gestionan bien muchas menos conexiones simultáneas de lo que la mayoría supone. Una máquina Postgres cómoda con 100 conexiones activas puede desplomarse con 500, porque cada conexión acarrea una carga real de memoria y de planificación. Las capas de aplicación serverless y con autoescalado empeoran esto: cada nueva instancia abre su propio pool y el número de conexiones se dispara.

Coloca un pooler por delante (PgBouncer para Postgres, ProxySQL para MySQL, o el equivalente gestionado de tu proveedor de nube). Usa pooling a nivel de transacción para que las conexiones se compartan de forma agresiva. Limita el tamaño del pool de forma deliberada y dimensiona los pools de la capa de aplicación teniendo en cuenta el límite real de la base de datos, no el número que trae por defecto tu framework de aplicación.

3. Caché donde las lecturas se repiten

Si los mismos datos se leen mucho más a menudo de lo que cambian, una caché por delante de la base de datos absorbe la carga. Redis o Memcached para búsquedas frecuentes de clave-valor, caché HTTP y de CDN para todo lo que pueda servirse ligeramente desactualizado, y vistas materializadas para agregaciones costosas que no necesitan ser en tiempo real.

La parte difícil de la caché es la invalidación, así que sé explícito sobre tu estrategia: expiración basada en el tiempo para datos que pueden estar un poco desactualizados, e invalidación basada en eventos para datos que deben estar frescos al escribir. Cachea las lecturas costosas, no todo, y mide siempre tu tasa de aciertos (hit rate). Una caché que funciona con una tasa de aciertos del 40 % a menudo añade latencia en lugar de quitarla.

4. Réplicas de lectura

Cuando las lecturas dominan y ya has cacheado las ganancias obvias, replica. Un primario gestiona las escrituras, una o más réplicas de lectura gestionan las consultas y la aplicación enruta en consecuencia. Las bases de datos gestionadas en la nube hacen que esto sea casi una casilla que marcar.

El compromiso es el retraso de replicación (replication lag). Una réplica es consistente en última instancia (eventually consistent), así que un usuario que acaba de guardar un cambio podría leer una réplica que no se ha puesto al día y ver datos desactualizados. Enruta hacia el primario las lecturas que deben ser inmediatamente consistentes (un usuario que lee su propio formulario recién enviado) y envía a las réplicas todo lo que tolera un pequeño retraso (paneles, listados, búsqueda). Las consultas de reportes y analítica en particular pertenecen a una réplica para que nunca compitan con las escrituras de producción.

5. Escalado vertical

A veces la solución más barata es una máquina más grande. El hardware moderno es enorme: instancias con cientos de gigabytes de RAM y decenas de núcleos son habituales, y un solo nodo bien afinado puede soportar cargas de trabajo que los equipos suponen que requieren un clúster. Añadir RAM para que el conjunto de trabajo quepa en memoria a menudo logra más que cualquier cambio de topología.

El escalado vertical tiene un techo y una factura, así que no es el estado final. Pero como forma de ganar entre seis y doce meses mientras construyes el siguiente peldaño con calma, con frecuencia es la decisión correcta. Solo asegúrate de que tu proveedor admita el redimensionamiento con un tiempo de inactividad mínimo antes de apoyarte en ello, y vigila la curva de costes. Nuestra guía práctica de optimización de costes en la nube explica cómo evitar que ese gasto se te escape sin darte cuenta.

6. Particionamiento dentro de una sola base de datos

El particionamiento divide una tabla grande en piezas físicas más pequeñas mientras la mantiene como una única tabla lógica y permanece dentro de una sola base de datos. Esto no es sharding: no hay coordinación distribuida y tus consultas apenas cambian.

La ganancia clásica es el particionamiento basado en el tiempo sobre una tabla de eventos, registros o pedidos: particiona por mes y las consultas filtradas por un rango de fechas solo escanean las particiones pertinentes (poda de particiones, partition pruning). Eliminar datos antiguos se convierte en un descarte de partición instantáneo en lugar de un DELETE lento y que infla la tabla. También puedes particionar por una clave como inquilino (tenant) o región. El particionamiento ofrece buena parte del beneficio que la gente espera del sharding mientras conserva la sencillez operativa de una sola base de datos.

7. Separar por dominio

Antes de repartir una sola tabla entre muchas máquinas, plantéate repartir tu esquema entre unas pocas. Si la facturación, la analítica y el producto principal viven todos en una sola base de datos y compiten por los mismos recursos, separarlos por contexto delimitado (bounded context) da a cada uno espacio para respirar y escalar según sus propios términos.

Esto encaja de forma natural con las fronteras de servicios. Si ya te mueves en esa dirección, nuestra guía sobre el paso del monolito a los microservicios explica cómo trazar esas costuras sin crear un caos distribuido. Una separación CQRS (un almacén de escritura normalizado más un almacén optimizado para la lectura, a menudo un motor de búsqueda o una réplica desnormalizada) es la misma idea aplicada a un único dominio de alto tráfico.

Cuándo tiene sentido de verdad el sharding

A veces lo necesitas de verdad. Las señales honestas:

  • Una sola tabla ha crecido más allá de lo que un nodo puede contener o indexar con eficiencia, incluso después de particionar.
  • El rendimiento de escritura en el primario es el cuello de botella, y las réplicas no ayudan porque solo escalan las lecturas.
  • Tienes una clave de sharding natural y estable (ID de inquilino, ID de usuario, región) que mantiene casi todas las consultas dentro de un solo fragmento.
  • Ya has subido todos los peldaños anteriores y sigues teniendo un muro delante.

Si ese es tu caso, haz sharding de forma deliberada: elige una clave de sharding que coincida con tus patrones de acceso, planifica el reequilibrado desde el primer día y apóyate en un sistema que gestione la distribución por ti (Citus, Vitess o una base de datos distribuida de forma nativa) en lugar de programar el enrutamiento a mano en el código de la aplicación.

Una lista de comprobación previa al sharding

Repasa esta lista antes de que nadie abra un documento de diseño de sharding:

  1. ¿Están indexadas las 20 consultas más lentas y libres de escaneos secuenciales sobre tablas grandes?
  2. ¿Se han eliminado los patrones N+1 en los endpoints más solicitados?
  3. ¿Hay un pooler de conexiones instalado con tamaños de pool deliberados?
  4. ¿Están cacheadas las lecturas repetidas, con una tasa de aciertos que realmente has medido?
  5. ¿Se han trasladado las consultas de mucha lectura y de reportes a réplicas?
  6. ¿Se ha probado el escalado vertical o al menos se ha presupuestado?
  7. ¿Están particionadas las tablas grandes de series temporales o con muchas inserciones?
  8. ¿Has separado los dominios no relacionados en sus propios almacenes?

Si no puedes marcar la mayoría de estas casillas, el sharding no resolverá tu problema. Añadirá un problema de sistemas distribuidos por encima del que ya tienes.

Cómo puede ayudar Innovation T

La mayor parte del dolor de bases de datos que nos llaman a arreglar no es un problema de capacidad, es un problema de instrumentación y consultas disfrazado de problema de capacidad. En Innovation T, nuestros equipos de ingeniería de software y de nube empiezan donde apuntan las cifras: perfilamos la carga de trabajo real, arreglamos las consultas e índices que de verdad duelen y luego subimos la escalera (pooling, caché, réplicas, particionamiento) para que solo añadas complejidad donde se la gana. Cuando el sharding o una base de datos distribuida es realmente la respuesta, diseñamos contigo la clave de sharding y la ruta de migración para que aguanten años, no meses.

Si tu base de datos empieza a sentirse como un techo, echa un vistazo a nuestros servicios o ponte en contacto. Preferimos ayudarte a evitar un fragmento que limpiar después de uno hecho con prisas.

#base de datos#escalado#rendimiento#arquitectura

¿Listo para construir con Innovation T?

Ya se trate de seguridad, crecimiento o ingeniería, nuestro equipo puede ayudarte a lograrlo con calidad.