Cloud Intelligence™Cloud Intelligence™

Cloud Intelligence™

DynamoDB: lo que la mayoría de los equipos hace mal antes de escribir una sola línea de código

Esta página también está disponible en English, Deutsch, Français, Italiano, 日本語 y Português.

By Joseph AllamJul 20, 20268 min read
Joseph Allam

About Joseph Allam

Databases have been my craft for a long time — long before the cloud, and deep into it across AWS and GCP. I know what breaks, what costs too much, and what needs rethinking before it hurts. I also know how to bring AI into the work: into how databases are designed, operated, and accessed by the intelligent systems being built on top of them.

My personal page

En el Prime Day de 2025, Amazon DynamoDB alcanzó un pico de 151 millones de solicitudes por segundo. Es la base de datos detrás de algunos de los workloads más exigentes del planeta, y lo ha sido durante casi dos décadas. La mayoría de los ingenieros que trabajan con AWS ha oído hablar de ella. Muchos la han usado. Pero en el DynamoDB Immersion Day que impartí hace poco junto al equipo de AWS, el mismo problema apareció en casi todas las sesiones: equipos que habían elegido DynamoDB por las razones correctas seguían batallando porque estaban diseñando sus esquemas de la manera equivocada.

De eso trata este artículo. No de los fundamentos de DynamoDB (de eso hay de sobra en otros lados), sino del enfoque de diseño que separa a los equipos que la hacen funcionar bien de los que terminan con costos fuera de control y tablas que no escalan.

PerfectScale™ for Kubernetes

Ready to optimize?

Get your free Kubernetes savings analysis

¿Es DynamoDB la opción correcta?

Antes de entrar en el diseño, conviene ser directos sobre cuándo tiene sentido usarla. DynamoDB es una buena opción cuando necesitas una latencia constante de milisegundos de un solo dígito prácticamente a cualquier escala, tus patrones de acceso se conocen y son predecibles desde el inicio, y tu workload es intensivo en lecturas y escrituras más que analíticamente complejo. Buenos ejemplos: APIs de cara al usuario, almacenamiento de sesiones, tablas de clasificación, carritos de compra, ingesta de IoT, event sourcing.

Es una mala opción cuando necesitas consultas ad-hoc en muchas dimensiones, tus patrones de acceso siguen evolucionando o trabajas con joins relacionales complejos. Si estás reemplazando una base de datos de reportes o construyendo algo donde los requisitos de consulta cambiarán mes a mes, vas a estar peleando con DynamoDB a cada paso.

El tamaño de los ítems importa más de lo que la mayoría espera

Antes de comprometerte con DynamoDB, modela bien el costo. DynamoDB cobra la capacidad de escritura en incrementos de 1 KB, redondeados hacia arriba. En modo on-demand, un ítem de 20 KB consume 20 Write Request Units (WRU); en modo aprovisionado, la misma escritura consume 20 Write Capacity Units (WCU). Las lecturas funcionan de forma similar, facturadas en incrementos de 4 KB por Read Capacity Unit (RCU), así que el tamaño del ítem determina ambos lados de tu costo de capacidad.

Suena simple, pero el tamaño de los ítems es una de las variables que más se pasan por alto cuando los equipos modelan los costos de DynamoDB. Con frecuencia vemos clientes que migran desde bases de datos relacionales como PostgreSQL, donde el concepto de facturación por byte en las escrituras no existe, y sus estimaciones iniciales de costo quedan muy lejos de la realidad porque no consideraron correctamente el tamaño de los ítems. Acertar con ese número antes de comprometerte cambia por completo la ecuación económica.

No se trata de que DynamoDB sea cara. Se trata de que el modelo de costos es distinto del que la mayoría de los ingenieros conoce, y las variables que lo determinan (el tamaño de los ítems, la eficiencia de los patrones de acceso, capacidad on-demand vs. aprovisionada) deben entenderse antes de comprometerse, no después.

El cambio de modelo mental que lo transforma todo

La mayoría de los ingenieros llega a DynamoDB con un hábito del mundo relacional: diseñar primero el esquema y resolver las consultas después.

Con una base de datos relacional, eso funciona bastante bien. Normalizas los datos, construyes índices más adelante y escribes consultas que hacen joins entre tablas. El motor de la base de datos se encarga de gran parte de la complejidad de las consultas.

DynamoDB no funciona así. A diferencia de las bases de datos relacionales, no optimiza consultas arbitrarias mediante un planificador de consultas. En cambio, enruta cada solicitud directamente a la partición dueña de los datos usando el hash de la partition key, y esa es una de las razones por las que DynamoDB puede entregar de forma constante latencias de milisegundos de un solo dígito sin importar el tamaño de la tabla. La contrapartida es que la base de datos está optimizada para una sola cosa: recuperar ítems de forma eficiente mediante patrones de acceso basados en claves, a través de la clave primaria o de índices secundarios. Todo lo demás (filtrar, ordenar por atributos que no son clave, consultar relaciones entre entidades) es costoso o requiere índices adicionales diseñados con anticipación.

El cambio es este: necesitas conocer tus patrones de acceso antes de diseñar tu tabla. No a grandes rasgos. Con precisión. "Obtener todos los pedidos de un usuario, ordenados por fecha" es un insumo de diseño. "Obtener los 10 tickets de soporte escalados más recientes de una cuenta determinada" es un insumo de diseño. Tu esquema se construye a partir de esas preguntas, no a partir de la forma de los datos.

En la práctica, esto significa que una sola tabla de DynamoDB a menudo almacena varios tipos de entidades juntos, con partition keys y sort keys construidas para responder preguntas específicas de forma eficiente. Eso le parece un error a cualquiera que venga de un mundo SQL normalizado. No lo es. Es justamente la idea.

Sort keys compuestas

Tomemos un ejemplo común: los pedidos de un usuario, donde necesitas consultar por estado y fecha. El enfoque ingenuo almacena el estado y la fecha como atributos separados y luego filtra al momento de la lectura. Terminas leyendo todos los pedidos de ese usuario y descartando los que no coinciden.

El enfoque que parte de los patrones de acceso codifica la consulta directamente en la sort key:

PK SK
user#123 ACTIVE#2024-07-15
user#123 ACTIVE#2024-07-01
user#123 COMPLETED#2024-06-20
user#123 COMPLETED#2024-06-10

Ahora una sola consulta con BEGINS_WITH("ACTIVE#") devuelve solo los pedidos activos, ordenados por fecha, sin leer un solo pedido completado. Pagas exactamente por lo que recuperas.

Esto funciona porque BEGINS_WITH es una consulta de prefijo sobre la sort key. La agrupación más amplia debe ir primero para que DynamoDB pueda usarla para acotar el rango. Un enfoque común es colocar la condición más amplia a la izquierda y los componentes progresivamente más específicos a la derecha, de modo que las consultas de prefijo puedan apuntar de forma eficiente al subconjunto que te interesa.

Índices secundarios globales dispersos

Otro patrón que surge de pensar primero en los patrones de acceso: si solo una pequeña fracción de tus ítems llega a cumplir una condición (tickets escalados, registros marcados, ítems pendientes de revisión), modela esa condición como un atributo que solo existe en esos ítems. Un GSI construido sobre ese atributo solo indexa los ítems donde está presente.

El comportamiento clave aquí es que DynamoDB no incluye un ítem en un GSI cuando el atributo indexado está ausente (no solo nulo, sino completamente ausente del ítem). Esto significa que puedes hacer que un GSI sea disperso por diseño simplemente omitiendo el atributo en los ítems que no quieres indexar.

Si el 2% de tus tickets están escalados, el GSI contiene el 2% de la tabla. Consultarlo es muchísimo más barato que escanear la tabla completa y filtrar. El GSI es disperso por diseño, no por accidente.

Your cloud bill shouldn't be a mystery

Optimization, automation, expertise. In one platform.

Los errores que aparecen en tu factura

Vale la pena nombrarlos específicamente porque son comunes, son invisibles hasta que revisas tus reportes de costos y suelen introducirlos ingenieros que entienden DynamoDB razonablemente bien.

Expresiones de filtro

DynamoDB sí permite filtrar los resultados de una consulta después de recuperarlos. El problema es que una FilterExpression no reduce las unidades de capacidad consumidas; solo reduce lo que se devuelve a quien realiza la llamada. Si una consulta lee 500 ítems antes de que se ejecute el filtro, se te cobra por todos los datos leídos hasta ese punto, sin importar cuántos ítems se devuelvan.

Esto se vuelve caro rápidamente cuando es la estrategia principal para acotar resultados. Se parece mucho a una cláusula WHERE de SQL, así que es fácil recurrir a ella sin pensar en el costo. La solución casi siempre es rediseñar la estructura de claves para que la base de datos recupere solo lo que necesitas. Las expresiones de filtro tienen su lugar, pero no deberían ser el pilar de tu estrategia de consultas.

Particiones calientes por claves basadas en tiempo

Un patrón común al construir datos de series temporales: usar el timestamp actual o la hora actual como partition key. El razonamiento parece intuitivo: particionar por bloque de tiempo. El problema es que todas las escrituras apuntan a la misma partition key lógica, lo que impide que DynamoDB distribuya el tráfico de escritura de manera uniforme entre particiones. A cualquier volumen de escritura significativo, esto provoca throttling.

DynamoDB sí cuenta con Adaptive Capacity, que redistribuye automáticamente el throughput hacia las particiones que reciben más tráfico. Ante un desbalance moderado, a menudo resuelve el problema de forma transparente. Pero ninguna capacidad adaptativa compensa que una sola partition key reciba tráfico extremo sostenido, y una clave por bloque de tiempo bajo alta carga de escritura es exactamente esa situación.

La solución es el write sharding: agrega un sufijo aleatorio a la partition key (por ejemplo, EVENTS#4, donde el sufijo es un número entre 0 y N) para repartir las escrituras entre varias particiones lógicas. Al momento de leer, consulta cada shard en paralelo y combina los resultados. Requiere más código en la aplicación, pero es lo que evita que un workload con alto throughput de escritura se atasque en una sola partición.

Escaneos de tabla

El tercero que vale la pena señalar: los escaneos completos de tabla. DynamoDB soporta operaciones Scan, y a veces los ingenieros recurren a ellas cuando se quedan sin GSIs o necesitan consultar por un atributo que no está indexado. El problema es que un escaneo lee cada ítem de la tabla sin importar cuántos coincidan. En una tabla grande, esto es lento y caro a la vez.

Los escaneos son perfectamente razonables para migraciones, exportaciones, tareas administrativas o procesamiento en segundo plano donde la latencia no importa. El problema es usarlos en rutas de solicitud de cara al usuario. Si un escaneo está en el flujo crítico de tu aplicación, casi siempre es una señal de que los patrones de acceso no se consideraron por completo en el diseño del esquema. La solución suele ser un nuevo GSI o una sort key rediseñada, no un escaneo más rápido.

Reflexiones finales

DynamoDB es genuinamente potente y, para los workloads adecuados, es una de las mejores bases de datos que ofrece AWS. Pero la brecha entre "elegir la base de datos correcta" y "diseñarla correctamente" es amplia, y esa brecha aparece en tu factura de AWS antes que en cualquier otro lugar. El modelo de costos es distinto, el proceso de diseño es distinto y los errores son distintos de los que la mayoría de los ingenieros ha enfrentado antes.

Si estás evaluando DynamoDB para un nuevo workload, o ya tienes tablas en producción y los costos son más difíciles de explicar de lo que te gustaría, este es exactamente el tipo de trabajo que DoiT hace con clientes de AWS cada semana. Nuestro equipo de más de 100 expertos en la nube puede ayudarte a validar patrones de acceso, detectar problemas de diseño a tiempo y sacar el máximo provecho de los servicios que ya estás pagando. Solicita una demo.