Les stratégies de filtrage DynamoDB
« Filtrer » dans DynamoDB désigne quatre choses différentes portant le même mot. Trois
réduisent les données avant qu'elles soient lues et facturées ; une — celle qui s'appelle Filter —
les réduit après. Savoir laquelle est laquelle constitue l'essentiel de la compétence.
Comment fonctionne le filtrage dans DynamoDB ?
DynamoDB a quatre façons de filtrer, et une seule s'exécute après que tu es facturé. La choisit une partition, la clé de tri réduit une tranche, et un index clairsemé filtre par présence d'attribut — les trois réduisent ton coût de lecture avant le métrage. Une FilterExpression s'exécute après la lecture, donc elle réduit la réponse mais jamais la facture.
- La est le filtre le moins cher : elle choisit la partition, donc tu ne touches jamais le reste de la table.
- La filtre au sein d'une partition avec
begins_with,between,<,>— toujours avant la facturation, toujours bon marché. - L' filtre par absence : un item n'apparaît dans l'index que s'il possède l'attribut indexé, donc l'index est l'ensemble filtré.
- La
FilterExpressionest le piège : elle s'exécute après que DynamoDB métre la lecture, donc elle réduit la taille de ta réponse mais jamais ta facture.
Mettre en place l'exemple
Un catalogue de produits. Une table, clé de partition PK, clé de tri SK :
PK = "DEPT#kitchen" SK = "PROD#00194"
Chaque produit porte aussi price, inStock (un booléen) et clearanceAt
(un horodatage unix, présent uniquement sur les items marqués pour déstockage). Les items d'un
rayon partagent une partition, triés par id de produit.
Nous voulons quatre motifs d'accès. Chacun correspond à une stratégie de filtrage différente —
et le mauvais choix sur l'un d'eux est un Scan que tu paieras pour toujours.
Filtrer par clé de partition
« Donne-moi tous les produits du rayon kitchen. » La clé de partition y répond directement :
Query PK = "DEPT#kitchen"
DynamoDB lit exactement une partition. Rien d'autre dans la table n'est touché ni
facturé. C'est le seul filtre gratuit au sens qui compte — c'est la
différence entre Query et Scan.
En venant de SQL, cela paraît à l'envers : il n'y a pas de WHERE department = 'kitchen'
qui balaie un index, tu nommes simplement la partition. Si tu ne peux pas la nommer, c'est un
problème de modélisation, pas un problème de requête.
Filtrer par clé de tri
« Donne-moi les produits du rayon kitchen à partir de PROD#00100. » La clé de tri réduit à l'intérieur
de la partition, et elle le fait avant que la lecture soit métrée :
Query PK = "DEPT#kitchen" AND SK between "PROD#00100" AND "PROD#00200"
Les conditions de clé de tri sont limitées à dessein : =, <, <=, >, >=,
between et begins_with. Pas de OR, pas de prédicat arbitraire.
Cette contrainte est ce qui garde la lecture ciblée — DynamoDB parcourt une tranche contiguë, pas toute la partition.
Le levier ici est comment tu encodes la clé de tri. Si ton motif est « par tranche de
prix », une clé de tri PROD#<id> n'aidera pas — tu encoderais le prix dans la clé.
C'est une décision de stratégie de clé de tri, prise au moment de la conception, pas au moment de la requête.
Filtrer par index clairsemé
« Donne-moi tout ce qui est actuellement en déstockage. » La plupart des produits ne le sont pas, donc tu ne veux pas lire le catalogue pour trouver les rares qui le sont.
Un index clairsemé résout cela par l'absence. Un ne contient un item que si cet item possède les deux attributs de clé de l'index.
Définis un drapeau constant clearance = "CLEARANCE" comme clé de partition du GSI —
écrit uniquement sur les items en déstockage — avec clearanceAt comme clé de tri, et
l'index ne contient rien d'autre.
AWS le précise : un index secondaire global ne contient que les items qui ont les attributs de clé de l'index, donc les items dépourvus de l'attribut de clé ne sont simplement pas propagés (AWS — Tirer parti des index clairsemés).
Maintenant la requête ne lit que les items en déstockage, facturés seulement pour eux :
Query ON ClearanceIndex GSI_PK = "CLEARANCE" (sorted by clearanceAt)
Le filtre s'est produit quand tu as écrit les données — en choisissant de définir ou non
clearanceAt. L'index est l'ensemble filtré. Voir
GSI vs LSI pour savoir quel type d'index convient.
Filtrer avec FilterExpression
« Donne-moi les produits du rayon kitchen qui sont en stock. » inStock n'est pas un attribut de clé,
donc tu te tournes vers une FilterExpression :
Query PK = "DEPT#kitchen"
Filter inStock = true
Voici le piège. DynamoDB lit chaque item de la partition kitchen, métre
la capacité pour tous, et ensuite écarte ceux qui sont en rupture de stock.
La règle officielle : une expression de filtre est « appliquée après qu'une Query se termine, mais
avant que les résultats soient renvoyés », et « une Query consomme la même quantité de capacité de
lecture, qu'une expression de filtre soit présente ou non » — tu as déjà
payé pour la lecture complète (AWS — Expressions de filtre pour Query).
Donc si kitchen compte 10 000 produits et que 12 sont en stock, tu paies pour lire 10 000.
La réponse est petite ; la facture ne l'est pas. FilterExpression réduit la charge utile
qui traverse le fil, jamais la lecture.
Il y a une seconde arête, plus tranchante : la pagination est métrée avant le filtrage. Une page fait 1 Mo d'items lus, pas 1 Mo de correspondances.
Un filtre peut renvoyer une page vide avec un LastEvaluatedKey défini — DynamoDB a lu un
mégaoctet complet, n'a rien fait correspondre, t'a rendu un tableau vide. Tu continues de paginer, et
tu as payé pour chaque page vide.
Construis l'expression — noms, valeurs et le bon échappement de mot réservé — avec
le Générateur d'expressions DynamoDB pour que les
espaces réservés #inStock/:val soient corrects du premier coup.
Le générateur ci-dessous est préréglé sur un Scan avec une FilterExpression — l'exact
anti-motif ci-dessus. Remarque que le filtre s'exécute sur toute la table, pas sur une tranche de clé :
Comparer les quatre
| Quand ça filtre | Réduit le coût de lecture ? | Puissance du prédicat | Coût de mise en place | |
|---|---|---|---|---|
| Clé de partition | Avant la lecture | Oui — une partition | Égalité uniquement | Gratuit (c'est la clé) |
| Clé de tri | Avant la lecture | Oui — une tranche | Plage / begins_with | Conception de la clé de tri |
| Index clairsemé | Avant la lecture | Oui — index seulement | Présence d'un attribut | GSI supplémentaire + coût d'écriture |
| FilterExpression | Après la lecture | Non | Presque n'importe quelle condition | Aucun |
Lis le tableau de haut en bas : la puissance du prédicat monte, le contrôle du coût
descend. FilterExpression peut tout exprimer précisément parce qu'elle s'exécute sur des
items déjà lus — c'est pour la même raison qu'elle ne peut pas t'économiser d'argent.
Vois-le dans DynoTable
Quand tu exécutes une Query avec un filtre, l'écart entre les items lus et les items
renvoyés est toute l'histoire. DynoTable affiche les items balayés à côté des items
renvoyés à mesure qu'une lecture filtrée arrive en flux — ainsi un filtre qui lit discrètement toute la partition est visible, et non
caché dans ta facture mensuelle.
Pour de véritables questions inter-items qu'un filtre ne peut pas résoudre — « prix moyen par
rayon », « produits en stock joints à leurs avis » — le SQL Workbench de DynoTable
exécute GROUP BY, JOIN et des agrégats côté client sur un ensemble de résultats borné,
au lieu de compiler vers un Scan sur toute la table.
Pièges et étapes suivantes
- N'utilise pas
FilterExpressioncomme chemin d'accès principal. Si un motif est courant, modélise-le dans une clé ou un index clairsemé. Un filtre sert au dernier petit bout d'affinage, pas à l'essentiel. - Surveille les pages vides. Une requête filtrée peut paginer longtemps en ne renvoyant
rien. Honore
LastEvaluatedKey; ne suppose pas qu'une page vide signifie « terminé ». - Un index clairsemé n'est pas gratuit. Il coûte de la capacité d'écriture et du stockage pour chaque item qui y atterrit — bon marché quand l'attribut est rare, moins quand il ne l'est pas.
Estime ce qu'une lecture filtrée coûtera réellement avec le calculateur de tarification, et essaie DynoTable pour observer la capacité consommée face aux lignes renvoyées sur tes propres tables.