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
FilterExpressiones 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).
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 predicado | Coste de configuración | |
|---|---|---|---|---|
| Clave de partición | Antes de leer | Sí — una partición | Solo igualdad | Gratis (es la clave) |
| Clave de ordenación | Antes de leer | Sí — una porción | Rango / begins_with | Diseño de la clave de ordenación |
| Índice disperso | Antes de leer | Sí — solo el índice | Presencia de un atributo | GSI extra + coste de escritura |
| FilterExpression | Después de leer | No | Casi cualquier condición | Ninguno |
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
FilterExpressioncomo 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.