Intermedio8 min de lectura

Índices dispersos de DynamoDB

Un índice disperso es un índice secundario que contiene solo los elementos que llevan su atributo de clave — así un subconjunto pequeño y caliente de una tabla enorme se convierte en su propia colección prefiltrada y lista para consultar.

Tienes millones de filas pero la consulta que ejecutas todo el día toca una porción diminuta: los tickets de soporte abiertos, las facturas impagadas, las cuentas marcadas para revisión.

Filtrar esa porción aún escanea toda la tabla y te factura cada lectura. Un índice disperso hace que el índice en sí sea pequeño.

¿Qué es un índice disperso en DynamoDB?

Un índice disperso es un índice secundario que contiene solo los elementos que llevan su atributo de clave. Como DynamoDB omite cualquier elemento al que le falte esa clave, inventas una clave que solo los elementos deseados escriben — tickets abiertos, facturas impagadas — y el índice se convierte exactamente en ese subconjunto. Las consultas leen solo él, sin filtro, sin capacidad de lectura desperdiciada.

  • Un índice secundario solo indexa los elementos que tienen su clave. Omite la clave en un elemento y nunca entra en el índice — sin marcador de posición, sin fila nula.
  • Así que inventas una clave que solo los elementos deseados llevan. Escríbela en los elementos que consultas, quítala en el resto. El índice se convierte exactamente en ese subconjunto.
  • La consulta lee solo el subconjunto, sin filtro. Su tamaño sigue al conjunto caliente pequeño, no al total de la tabla.
  • REMOVE es la palanca, no dejarlo en blanco. Una cadena vacía sigue siendo un valor y aún se indexa — debes eliminar el atributo.

El problema: filtrar no ahorra lecturas

Viniendo de SQL, asumes que una cláusula WHERE reduce el trabajo. La FilterExpression de DynamoDB no lo hace. Se ejecuta después de que los elementos se leen, no antes.

Según la AWS Developer Guide, filtrar "no reduce la cantidad de capacidad de lectura consumida" — pagas por cada elemento examinado, luego tiras los que no coinciden.

Así que si 50 de tus 5 millones de tickets están abiertos, una Query/Scan filtrada lee millones para entregarte esos 50.

Ese es el tiro al pie detrás de cada hilo de "por qué mi escaneo es tan caro"; query vs. scan tiene el panorama completo de costes.

Un índice disperso lo esquiva haciendo que el índice en sí sea pequeño.

Cómo funciona la dispersión

Un índice secundario solo indexa los elementos que realmente tienen los atributos de clave del índice.

La documentación de AWS sobre índices secundarios globales lo dice sin rodeos: "un índice secundario global solo contiene los elementos que tienen los atributos de clave definidos para ese índice".

Si falta la partition key (o sort key) del GSI en un elemento, DynamoDB simplemente no lo escribe en el índice. Sin marcador de posición, sin fila nula — el elemento está ausente.

Esa "ausencia por defecto" es todo el truco. No indexes un atributo status que cada elemento lleva. Inventa un atributo que solo los elementos que quieres consultar lleven en absoluto.

El índice se convierte entonces en una lista limpia de exactamente esos elementos, y una Query contra él lee solo a ellos — sin filtro, sin capacidad desperdiciada.

Imagina la tabla base alimentando el índice, donde solo cruzan los elementos que llevan la clave:

GSI disperso (solo abiertos)Tabla base (todos los elementos)clave eliminadaclave eliminadaAbierto: tiene la claveAbierto: tiene la claveCerrado: sin claveCerrado: sin claveAbiertoAbierto

Solo los elementos con clave (abiertos) se replican al índice; los elementos cerrados nunca entran en él.

Esta es la misma mentalidad de dar-forma-a-las-claves que el diseño de tabla única: las claves son herramientas que construyes para un patrón de acceso específico, no espejos fieles de tus datos.

Un ejemplo desarrollado: "solo tickets abiertos"

Toma una tabla de tickets de soporte. La tabla base está indexada para traer un ticket por id y listar los tickets de un cliente:

PKSKattributes
TICKET#a91fDETAILsubject, body, priority, openState
CUSTOMER#88TICKET#a91fsubject, priority, openState

A lo largo de la vida de la tabla, la mayoría de los tickets acaban cerrados. Pero la consulta del panel que tus agentes ejecutan todo el día es "muéstrame cada ticket abierto, el más antiguo primero" — unos cientos de filas escondidas dentro de millones.

El movimiento del índice disperso: define un GSI con partition key openBucket y sort key openedAt, y escribe openBucket solo en los tickets abiertos. Establécelo cuando se crea el ticket; REMOVE-lo cuando el ticket se resuelve.

PKSKopenBucketopenedAt
TICKET#a91fDETAILOPEN2026-06-23T09:14:00Z← open: in the index
TICKET#b02cDETAILOPEN2026-06-22T16:40:00Z← open: in the index
TICKET#77deDETAIL(absent)2026-05-30T11:02:00Z← closed: NOT in the index

Los tickets a91f y b02c llevan openBucket, así que viven en el GSI. El ticket 77de se resolvió y se le eliminó openBucket, así que cayó silenciosamente. El panel es ahora una sola consulta barata:

Query  IndexName = "open-tickets-index"
KeyConditionExpression: openBucket = "OPEN"
ScanIndexForward: true        # oldest first

Esto lee solo los tickets abiertos. A medida que los tickets se cierran, el índice se encoge por sí solo — su tamaño sigue a la población abierta, nunca al total.

Un único valor de partición estático ("OPEN") está bien aquí precisamente porque el conjunto se mantiene pequeño. Un conjunto abierto enorme necesitaría una partition key fragmentada, pero el índice de "subconjunto pequeño" es exactamente donde un solo valor es la decisión correcta.

La transición que lo hace funcionar es una sola update expression — eliminar el atributo cuando el ticket se resuelve.

Prototipa esa cláusula REMOVE y la condición de clave tipada para el lado de lectura en el Constructor de expresiones de DynamoDB, en lugar de ensamblar tú mismo a mano los ExpressionAttributeNames y los marcadores :val.

Hazlo en DynoTable

La parte difícil de un índice disperso no es la lectura — es ver qué elementos entraron en el índice frente a cuáles cayeron silenciosamente.

DynoTable te deja cambiar una vista de tabla a un índice secundario y ver exactamente el subconjunto poblado. Así puedes confirmar que un ticket resuelto realmente abandonó open-tickets-index en lugar de quedarse con una clave obsoleta.

La tabla de tickets de soporte vista a través de su GSI disperso de tickets-abiertos en DynoTable, mostrando solo los elementos que llevan la clave openBucket.
La tabla de tickets de soporte vista a través de su GSI disperso de tickets-abiertos en DynoTable, mostrando solo los elementos que llevan la clave openBucket.

Trampas y próximos pasos

Algunas cosas que vigilar:

  • Elimina la clave, no la dejes en blanco. Una cadena vacía sigue siendo un valor, y DynamoDB indexará un elemento cuyo openBucket sea "". Para sacar un elemento del índice debes hacer REMOVE del atributo — establecerlo a un valor falsy lo mantiene dentro.
  • El índice es eventualmente consistente. Los GSI se actualizan de forma asíncrona, así que un ticket recién-resuelto puede aparecer brevemente todavía — las lecturas de GSI solo soportan consistencia eventual. No confíes en él para "¿está este ticket abierto ahora mismo?".
  • Atento a los atributos proyectados. Una Query sobre el índice devuelve solo los atributos proyectados en él. Si el panel necesita el asunto y la prioridad, proyéctalos — o paga un GetItem extra por el elemento base completo.
  • Esto es una fortaleza del GSI, no del LSI. Los índices secundarios locales comparten la partition key de la tabla base y no pueden descartar elementos selectivamente de esta manera. GSI vs. LSI desglosa el compromiso.

Los índices dispersos son una de las ideas más antiguas del modelo. El artículo Dynamo de Amazon de 2007 original construyó el almacén en torno a servir patrones de acceso conocidos y de alto volumen de forma barata.

Un índice disperso es exactamente eso: da forma a las claves para que la consulta común no lea nada que no necesite.

Para construir e inspeccionar uno de verdad, descarga DynoTable, apúntalo a tu tabla y cambia la vista de datos a tu GSI disperso — observa cómo el subconjunto se actualiza a medida que los elementos ganan y pierden la clave del índice.

Actualizado