Cómo modelar datos en DynamoDB
En SQL modelas primero entidades y relaciones, y luego confías en que el planificador de consultas ensamble más tarde lo que le pidas. DynamoDB invierte eso. Modelas las lecturas que ya sabes que harás, y las claves existen para servirlas.
No hay motor de joins ni planificador eligiendo una estrategia en tiempo de ejecución. Una Query lee una partición a lo largo de una clave, y ese es todo el contrato de rendimiento. Así que diseñas claves para patrones de acceso conocidos, no para un esquema pulcro.
AWS lo dice sin rodeos en su guía de buenas prácticas: "no deberías empezar a diseñar tu esquema hasta que sepas las preguntas que tendrá que responder".
Esta guía recorre todo el proceso sobre un dominio: un ranking de un juego multijugador que rastrea jugadores, las partidas que juegan y su clasificación por temporada. Vamos de una lista de preguntas a un esquema de claves funcional.
¿Cómo se modelan datos en DynamoDB?
Modela primero las lecturas, no las tablas. Enumera cada consulta que hace la app, luego diseña una y una para que cada pregunta se resuelva en una sola Query o GetItem. Coubica los elementos que se leen juntos, recorre rangos de valores en la clave de ordenación y añade un GSI para cualquier patrón de acceso que la tabla base no pueda servir.
- Enumera primero las lecturas, no las tablas. Las preguntas son la especificación; los sustantivos son una distracción.
- Cada pregunta debe ser una
Queryo unGetItem. Si una pregunta necesita unScan, el modelo está mal. - Los elementos coubicados comparten una ; todo aquello sobre lo que recorres un rango va en la .
- Una pregunta que la tabla base no puede responder se lleva un — nunca un
Scancon un filtro.
Paso 1 — Enmarca el problema como preguntas, no como tablas
Resiste el impulso de dibujar tablas players, matches y scores. Ese instinto es el hábito de SQL, y aquí está mal. En su lugar, anota cada lectura que la app realiza de verdad. Para nuestro ranking:
- Obtener el perfil de un jugador por su id.
- Listar las partidas recientes de un jugador, la más nueva primero.
- Mostrar los N mejores jugadores de una temporada dada, ordenados por rating.
- Buscar un jugador por su nombre público (p. ej. para una URL de perfil).
Estas cuatro preguntas — no los sustantivos — son la especificación. Cada una debe resolverse en una sola Query (o GetItem), porque esa es la única forma de acceso que DynamoDB sirve barata a escala.
Si una pregunta solo puede responderse escaneando la tabla, el modelo está mal, y lo notarás en latencia y coste — ver Query vs Scan para saber por qué un Scan es el error a evitar.
Todo el método es un pipeline corto y ordenado que ejecutas una vez por dominio:
Cada paso de abajo se corresponde con una caja: listar, enumerar, diseñar claves, añadir índices para el resto y luego validar.
Paso 2 — Entiende los primitivos con los que modelas
Una tabla tiene una clave de partición (PK) que elige en qué partición física vive un elemento, y una clave de ordenación (SK) opcional que ordena los elementos dentro de esa partición.
Los documentos de componentes básicos de AWS llaman al par la clave primaria del elemento. Una Query siempre apunta a exactamente un valor de PK y puede recorrer un rango o filtrar la SK — ese es todo el instrumental.
Este diseño de una sola partición es lo que permite a DynamoDB entregar las lecturas predecibles, de baja latencia y particionadas horizontalmente descritas por primera vez en el paper de Amazon Dynamo de 2007.
Dos consecuencias guían cada decisión de abajo:
- Los elementos que se leen juntos deben compartir una clave de partición para que una
Querylos devuelva en una sola petición facturada. - Todo aquello sobre lo que quieras recorrer un rango (partidas recientes, mejores ratings) debe vivir en la clave de ordenación, porque es el único atributo que
Querypuede ordenar y acotar.
Cuando una pregunta necesita una forma de acceso distinta de la que ofrece la tabla base, añades un índice secundario global — una reproyección de la tabla bajo una PK/SK distinta.
(Para GSI frente a índice secundario local, ver GSI vs LSI.)
Paso 3 — Diseña las claves, una pregunta cada vez
Usamos una sola tabla con atributos de clave genéricos y sobrecargados — el enfoque de tabla única — porque un jugador y sus partidas se leen juntos.
Inventa tus propios prefijos; aquí PLAYER#, MATCH# y SEASON# etiquetan el tipo de entidad dentro de claves por lo demás genéricas.
Las preguntas 1 y 2 (perfil + partidas recientes) comparten una partición, así que ambas cuelgan de la misma PK:
| partitionId | rangeId | attributes |
|---|---|---|
| PLAYER#u8231 | PROFILE | handle, region, createdAt |
| PLAYER#u8231 | MATCH#2026-06-23T14 | result=win, ratingDelta=+18, mapId |
| PLAYER#u8231 | MATCH#2026-06-23T11 | result=loss, ratingDelta=-15, mapId |
Query partitionId = "PLAYER#u8231" devuelve el perfil y cada partida en una sola lectura. Para el perfil solo, GetItem.
Para las partidas recientes, rangeId begins_with "MATCH#" con ScanIndexForward = false las recorre de la más nueva a la más antigua — la marca de tiempo en la clave de ordenación hace el ordenado gratis.
Las preguntas 3 y 4 no pueden responderse desde esa partición — pivotan sobre el rango de temporada y sobre el nombre, y ninguno de los dos es la PK base. Cada una se lleva un GSI.
Añadimos dos pares de atributos de índice genéricos — seasonPartition / seasonSort para el índice de rango y handlePartition / handleSort para el índice de nombre — poblados en el mismo elemento de perfil (el que escribimos en el Paso 3, ahora mostrado con sus atributos de índice rellenos):
| partitionId | rangeId | seasonPartition | seasonSort | handlePartition | handleSort |
|---|---|---|---|---|---|
| PLAYER#u8231 | PROFILE | SEASON#2026-Q2 | RATING#1842 | HANDLE#nighthawk | PLAYER#u8231 |
Ahora hacer Query al índice de temporada WHERE seasonPartition = "SEASON#2026-Q2" con ScanIndexForward = false devuelve los jugadores ordenados por rating — ese es el ranking.
Un segundo índice con clave en handlePartition = "HANDLE#…" resuelve un nombre público a un id de jugador en una sola lectura. Una tabla física, cuatro patrones de acceso de una sola Query.
Una nota de sobre
RATING#1842: DynamoDB ordena las claves de ordenación lexicográficamente, no numéricamente, así que un rating debe rellenarse con ceros a un ancho fijo (RATING#01842) o9ordenaría después de1000. Este es un error de modelado clásico que conviene resolver bien de antemano.
Paso 4 — Valida el modelo en DynoTable
Un esquema de claves solo se gana la confianza cuando ves una Query real devolver exactamente los elementos que esperabas y nada más.
Abre la tabla en DynoTable, ejecuta la consulta del ranking contra el índice de temporada, y confirma que la partición vuelve ordenada y acotada — sin Scan, sin ordenado en el cliente.

Cuando construyas las expresiones de condición para estas consultas — el begins_with, el seasonPartition = :p, el enlace del marcador :p — deja que el constructor de expresiones de DynamoDB lo haga.
Genera la KeyConditionExpression, los ExpressionAttributeNames y los ExpressionAttributeValues, de modo que una palabra reservada como result o un marcador mal escrito nunca rompa una lectura en silencio.
Paso 5 — Trampas y próximos pasos
Unas cuantas trampas que comprobar antes de enviar el modelo:
- No modeles relaciones que nunca lees juntas. Un GSI por pregunta es barato; un GSI desperdiciado es coste recurrente. Añade índices desde la lista de preguntas, no de forma especulativa.
- Vigila el calor de la partición. Si una PK (un jugador estrella, una única temporada caliente) absorbe la mayoría del tráfico, esa partición puede sufrir throttling. Reparte las escrituras con un fragmento de sufijo cuando una clave está demostrablemente caliente — AWS lo cubre en diseño de clave de partición.
- Rellena con ceros y usa ISO-8601 en todo lo numérico o temporal de una clave de ordenación, para que el orden lexicográfico coincida con el orden que quieres.
- Una pregunta nueva = una clave o índice nuevo, nunca un
Scan. Cuando aparezca más tarde un patrón de acceso genuinamente nuevo, extiende las claves; no lo tapes con un filtro.
Modela primero las preguntas, diseña claves para que cada una sea una Query, y luego demuéstralo.
Para llevar ventaja en el paso intermedio, la herramienta de diseño de tabla única gratuita convierte una lista de patrones de acceso como esta en un plan de PK/SK/GSI, con Items de ejemplo y pistas de coste.
Prueba DynoTable para explorar tu tabla, ejecutar estas consultas contra la tabla base y los GSI en paralelo, y ver los patrones de acceso que diseñaste devolver exactamente lo que planeaste. Y para la pregunta que no modelaste, su SQL Workbench ejecuta JOINs, GROUP BY y agregaciones reales en el lado del cliente.


