Sharding: una guía de decisión
El sharding es la decisión más irreversible de un sistema de datos. Esta es la guía para decidir si lo necesitas y, si lo necesitas, cómo elegir la clave.
Un índice equivocado lo tiras y creas otro. Una caché equivocada la apagas. Una clave de shard equivocada la vas a cargar durante años, y la corrección es un proyecto con dedicación de equipo, no una tarea de sprint.
Por eso este texto va menos sobre cómo hacerlo y más sobre cómo decidirlo. Y el primer consejo es impopular:
Las cuatro técnicas, y tres de ellas deberías agotarlas primero
El particionamiento vertical separa columnas. Las que siempre se leen juntas quedan en una tabla; las grandes y raramente leídas (el blob, el texto largo, el JSON de auditoría) van a otra.
Ganancia: en cada página del disco caben más filas útiles, y el escaneo se vuelve más rápido. Es la
técnica más barata y la más olvidada. En tablas con una columna text gigante, la ganancia es
inmediata.
El particionamiento funcional separa por dominio. Pedidos en una base, catálogo en otra, autenticación en otra. Es el camino natural hacia la separación en servicios, y suele dar más alivio que el sharding, con mucho menos dolor, porque cada pedazo sigue siendo una base normal, con transacciones normales.
El particionamiento horizontal nativo es lo que Postgres, MySQL y otros ofrecen como particiones
de tabla. Partes una tabla en pedazos dentro de la misma base, normalmente por fecha. La consulta que
filtra por fecha lee solo las particiones relevantes. Y borrar dato antiguo se convierte en un DROP
de partición, que es instantáneo, en vez de un DELETE masivo, que es doloroso.
Solo después de eso viene el sharding, que es distribuir las filas entre bases distintas.
Rango o hash
Dentro del sharding, la primera elección.
Por rango: clientes de la A a la M en un shard, de la N a la Z en otro. O por fecha.
- Ventaja: la consulta por rango sigue siendo eficiente, porque los vecinos están juntos.
- Desventaja: distribución desigual. Si particionas por fecha y todo el mundo escribe hoy, un shard se lleva toda la carga de escritura y los otros quedan ociosos.
Por hash: aplicas hash a la clave y distribuyes.
- Ventaja: distribución uniforme casi gratis.
- Desventaja: perdiste la consulta por rango, por el mismo motivo por el que una hash table no hace rangos: la función existe para esparcir.
La clave de shard: tres criterios
Esta es la decisión que domina a todas las demás.
Criterio 1: ¿distribuye bien?
Si el noventa por ciento del tráfico tiene el mismo valor de clave, no hiciste sharding, solo complicaste. Mira la distribución real, con datos de producción, antes de decidir.
Criterio 2: ¿tus consultas más frecuentes consiguen ir a un solo shard?
Si la consulta principal necesita preguntar a todos los shards y juntar las respuestas, cambiaste una query rápida por diez lentas más una agregación. Eso se llama scatter-gather, y es la manera más común de empeorarlo todo.
Y acuérdate de la amplificación de cola: con diez shards, la latencia de tu consulta pasa a ser la del shard más lento de los diez.
Criterio 3: ¿es estable?
Una clave que cambia de valor obliga a mover el registro de shard. Prefiere algo inmutable. El identificador de cliente es bueno; el estado del pedido es pésimo.
En la práctica, en un sistema de negocio, la clave suele ser el identificador del cliente o de la cuenta, porque casi toda consulta es "las cosas de este cliente", y eso cae en un solo shard.
Los tres problemas que crea el sharding
Y que raramente entran en la reunión de decisión.
Se acabaron las transacciones entre shards. La base garantiza ACID dentro de un shard. Entre
shards, necesitas una saga o un commit en dos fases. Toda operación que toca dos clientes a la vez
(una transferencia, por ejemplo) se convirtió en un proyecto en vez de un BEGIN/COMMIT.
Se acabaron los JOINs entre shards. Vas a desnormalizar a propósito, duplicando dato, y convivir con la inconsistencia de eso. No es un fallo de diseño; es el precio.
Se acabó la clave primaria automática. Una secuencia de base no funciona distribuida. Vas a usar UUID, ULID o algo tipo Snowflake.
Un detalle importante: si usas UUID versión 4, puramente aleatorio, dentro de un índice B-tree, prepárate para fragmentación y escritura esparcida. Prefiere algo ordenable en el tiempo, como ULID o UUIDv7, que mantiene la localidad de inserción.
Rebalanceo y hash consistente
La manera ingenua de distribuir es hash de la clave módulo el número de nodos. Funciona maravillosamente hasta que añades un nodo.
Con cuatro nodos, la clave va a hash módulo cuatro. Con cinco, a hash módulo cinco. Prácticamente toda clave cambia de lugar. Si es caché, invalidaste todo de una vez. Si es base de datos, necesitas mover casi todos los datos.
El hash consistente lo resuelve. Imagina un círculo numerado; cada nodo ocupa varios puntos de ese círculo, y cada clave pertenece al primer nodo en sentido horario. Al añadir un nodo, solo las claves entre él y el anterior cambian de dueño: aproximadamente una de cada N en vez de todas.
Los puntos múltiples por nodo (las réplicas virtuales) existen para uniformizar la distribución. Con pocos puntos, la mala suerte deja la distribución torcida; con ciento cincuenta por nodo, queda bien equilibrada.
Distribuir dato no es distribuir carga
Incluso con claves perfectamente distribuidas, el tráfico puede ser desigual.
Clave caliente: un registro específico recibe tráfico desproporcionado: el post del influencer, el producto en promoción, el cliente que es el diez por ciento de la facturación. El shard que hospeda esa clave se satura mientras los otros quedan ociosos.
Soluciones, en orden de simplicidad: caché delante para absorber lectura; replicar esa clave con sufijos y elegir uno al azar; para escritura concentrada, dividir en subclaves y agregar después; y, en último caso, dar un shard dedicado al cliente gigante, práctica común en sistemas multi-tenant.
Shard caliente: por diseño, un rango concentra la carga. El clásico es particionar por fecha.
Y la detección exige métrica por shard. Un panel con la media de los shards esconde exactamente el problema que estás buscando. Mira el máximo y la desviación.
La checklist de decisión
Antes de hacer sharding, responde por escrito:
- ¿Ya agoté índices, caché, réplicas, particionamiento vertical y funcional?
- ¿Cuál es la clave, y cómo se distribuye en mis datos reales?
- ¿Cuáles de mis cinco consultas más frecuentes dejan de funcionar en un solo shard?
- ¿Qué operaciones pasan a necesitar una saga?
- ¿Cómo rebalanceo cuando añada un shard?
- ¿Cómo detecto una clave caliente?