Intermedio8 min de lectura

Estrategias de filtrado en DynamoDB

"Filtrar" en DynamoDB significa cuatro cosas diferentes con la misma palabra. Tres estrechan los datos antes de que se lean y facturen; una — la llamada Filter — los estrecha después. Saber cuál es cuál es la mayor parte de la habilidad.

¿Cómo funciona el filtrado en DynamoDB?

DynamoDB tiene cuatro formas de filtrar, y solo una se ejecuta después de que se te factura. La clave de partición elige una partición, la clave de ordenación estrecha una porción y un índice disperso filtra por la presencia de un atributo — las tres recortan tu coste de lectura antes de la medición. Un FilterExpression se ejecuta después de la lectura, así que reduce la respuesta pero nunca la factura.

  • La clave de partición es el filtro más barato: elige la partición, así que nunca tocas el resto de la tabla.
  • La clave de ordenación filtra dentro de una partición con begins_with, between, <, > — aún antes de facturar, aún barato.
  • El índice disperso filtra por ausencia: un item solo aparece en el índice si tiene el atributo indexado, así que el índice es el conjunto filtrado.
  • El FilterExpression es la trampa: se ejecuta después de que DynamoDB mida la lectura, así que recorta el tamaño de tu respuesta pero nunca tu factura.

Prepara el ejemplo

Un catálogo de productos. Una tabla, clave de partición PK, clave de ordenación SK:

PK = "DEPT#kitchen"   SK = "PROD#00194"

Cada producto también lleva price, inStock (un booleano) y clearanceAt (una marca de tiempo unix, presente solo en items marcados para liquidación). Los items de un departamento comparten una partición, ordenados por id de producto.

Queremos cuatro patrones de acceso. Cada uno mapea a una estrategia de filtrado diferente — y la elección equivocada en cualquiera de ellos es un Scan que pagarás para siempre.

Filtrar por clave de partición

"Dame cada producto en kitchen." La clave de partición responde esto directamente:

Query  PK = "DEPT#kitchen"

DynamoDB lee exactamente una partición. Nada más en la tabla se toca ni se factura. Este es el único filtro que es gratis en el sentido que importa — es la diferencia entre Query y Scan.

Si vienes de SQL, esto se siente al revés: no hay un WHERE department = 'kitchen' escaneando un índice, simplemente nombras la partición. Si no puedes nombrarla, eso es un problema de modelado, no de consulta.

Filtrar por clave de ordenación

"Dame productos de kitchen desde PROD#00100 hacia arriba." La clave de ordenación estrecha dentro de la partición, y lo hace antes de que se mida la lectura:

Query  PK = "DEPT#kitchen"  AND  SK between "PROD#00100" AND "PROD#00200"

Las condiciones de clave de ordenación están limitadas a propósito: =, <, <=, >, >=, between y begins_with. Sin OR, sin predicados arbitrarios.

Esa restricción es lo que mantiene la lectura dirigida — DynamoDB recorre una porción contigua, no toda la partición.

La palanca aquí es cómo codificas la clave de ordenación. Si tu patrón es "por banda de precio", una clave de ordenación PROD#<id> no ayudará — incorporarías el precio en la clave.

Esa es una decisión de estrategia de clave de ordenación, tomada en tiempo de diseño, no en tiempo de consulta.

Filtrar por índice disperso

"Dame todo lo que está actualmente en liquidación." La mayoría de los productos no lo están, así que no quieres leer el catálogo para encontrar los pocos que sí.

Un índice disperso resuelve esto por ausencia. Un Global Secondary Index solo contiene un item si ese item tiene ambos atributos clave del índice.

Pon la clave de partición del GSI en clearanceAt — presente solo en items de liquidación — y el índice no contiene nada más.

AWS lo deja claro: un GSI "solo contiene items que tienen los atributos indexados", así que los items a los que les falta el atributo clave simplemente no se propagan (AWS — Aprovecha los índices dispersos).

NoTabla base todos los productos¿Tiene clearanceAt?Replicado a ClearanceIndexNo está en el índiceQuery del índice = solo items enliquidación

Ahora la consulta lee solo los items en liquidación, facturada solo por ellos:

Query  ON ClearanceIndex   GSI_PK = "CLEARANCE"   (sorted by clearanceAt)

El filtro ocurrió cuando escribiste los datos — al elegir si poner clearanceAt o no. El índice es el conjunto filtrado. Ver GSI vs LSI para qué tipo de índice encaja.

Filtrar con FilterExpression

"Dame productos de kitchen que estén en stock." inStock no es un atributo clave, así que recurres a un FilterExpression:

Query  PK = "DEPT#kitchen"
Filter inStock = true

Aquí está la trampa. DynamoDB lee cada item de la partición kitchen, mide la capacidad de todos ellos, y luego descarta los que no están en stock.

La regla oficial: una expresión de filtro se "aplica después de que un Query termina, pero antes de que se devuelvan los resultados", y "no consume unidades de capacidad de lectura adicionales" — ya pagaste por la lectura completa (AWS — Expresiones de filtro para Query).

Así que si kitchen tiene 10.000 productos y 12 están en stock, pagas por leer 10.000. La respuesta es pequeña; la factura no. FilterExpression reduce el payload que cruza el cable, nunca la lectura.

Hay un segundo filo, más afilado: la paginación se mide antes de filtrar. Una página es 1 MB de items leídos, no 1 MB de coincidencias.

Un filtro puede devolver una página vacía con un LastEvaluatedKey establecido — DynamoDB leyó un megabyte completo, no coincidió con nada, te entregó un array vacío. Sigues paginando, y pagaste por cada página vacía.

Construye la expresión — nombres, valores y el escape correcto de palabras reservadas — con el DynamoDB Expression Builder para que los placeholders #inStock/:val sean correctos al primer intento.

Compara las cuatro

Cuándo filtra¿Recorta el coste de lectura?Poder de predicadoCoste de configuración
Clave de particiónAntes de leerSí — una particiónSolo igualdadGratis (es la clave)
Clave de ordenaciónAntes de leerSí — una porciónRango / begins_withDiseño de la clave de ordenación
Índice dispersoAntes de leerSí — solo el índicePresencia de un atributoGSI extra + coste de escritura
FilterExpressionDespués de leerNoCasi cualquier condiciónNinguno

Lee la tabla de arriba abajo: el poder de predicado sube, el control de coste baja. FilterExpression puede expresar cualquier cosa con precisión porque se ejecuta sobre items ya leídos — esa es la misma razón por la que no puede ahorrarte dinero.

Míralo en DynoTable

Cuando ejecutas un Query con un filtro, la brecha entre los items leídos y los items devueltos es toda la historia. DynoTable muestra la capacidad consumida junto al conteo de resultados — así que un filtro que silenciosamente lee toda la partición es visible, no está escondido en tu factura mensual.

Para preguntas genuinas entre items que un filtro no puede responder — "precio medio por departamento", "productos en stock unidos a sus reseñas" — el SQL Workbench de DynoTable ejecuta GROUP BY, JOIN y agregaciones del lado del cliente sobre un conjunto de resultados acotado, en lugar de compilar a un Scan de toda la tabla.

Trampas y próximos pasos

  • No uses FilterExpression como tu ruta de acceso principal. Si un patrón es común, modélalo en una clave o un índice disperso. Un filtro es para el último poquito de estrechamiento, no para el grueso.
  • Vigila las páginas vacías. Una consulta filtrada puede paginar mucho tiempo sin devolver nada. Respeta LastEvaluatedKey; no asumas que una página vacía significa "terminado".
  • Un índice disperso no es gratis. Cuesta capacidad de escritura y almacenamiento por cada item que aterriza en él — barato cuando el atributo es raro, menos cuando no lo es.

Estima lo que costará realmente una lectura filtrada con la calculadora de capacidad, y prueba DynoTable para observar la capacidad consumida frente a las filas devueltas en tus propias tablas.

Actualizado