Les stratégies de filtrage DynamoDB
« Filtrer » dans DynamoDB désigne quatre choses différentes portant le même mot. Trois
restreignent les données avant qu'elles soient lues et facturées ; une — celle qui
s'appelle Filter — les restreint après. Savoir laquelle est laquelle, c'est
l'essentiel de la compétence.
Comment fonctionne le filtrage dans DynamoDB ?
DynamoDB offre quatre façons de filtrer, et une seule s'exécute après que tu sois facturé. La clé de partition choisit une partition, la clé de tri restreint une tranche, et un sparse index filtre par présence d'attribut — toutes les trois réduisent ton coût de lecture avant la facturation. Une FilterExpression s'exécute après la lecture : elle réduit donc la réponse, mais jamais la facture.
- La clé de partition est le filtre le moins cher : elle choisit la partition, donc tu ne touches jamais au reste de la table.
- La clé de tri filtre à l'intérieur d'une partition avec
begins_with,between,<,>— encore avant facturation, encore bon marché. - Le sparse index filtre par absence : un item n'apparaît dans l'index que s'il a l'attribut indexé, donc l'index est l'ensemble filtré.
FilterExpressionest le piège : il s'exécute après que DynamoDB a mesuré la lecture, donc il 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
timestamp unix, présent seulement sur les items marqués en déstockage). Les items d'un
rayon partagent une partition, triés par id de produit.
On veut quatre modes 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 à jamais.
Filtrer par clé de partition
« Donne-moi chaque produit 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 qui est gratuit au sens qui compte — c'est la différence
entre Query et Scan.
En venant de SQL, ça paraît à l'envers : il n'y a pas de WHERE department = 'kitchen'
qui scanne 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 de kitchen à partir de PROD#00100. » La clé de tri
restreint à l'intérieur de la partition, et elle le fait avant que la lecture soit
mesurée :
Query PK = "DEPT#kitchen" AND SK between "PROD#00100" AND "PROD#00200"
Les conditions de clé de tri sont limitées exprès : =, <, <=, >, >=,
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, c'est comment tu encodes la clé de tri. Si ton mode est « par
tranche de prix », une clé de tri PROD#<id> n'aidera pas — il faudrait intégrer 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 sparse index
« 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 sparse index résout ça par absence. Un Global Secondary Index ne contient un item que si cet item a les deux attributs de clé de l'index.
Mets la clé de partition du GSI à clearanceAt — présent seulement sur les items en
déstockage — et l'index ne contient rien d'autre.
AWS l'explicite : un GSI « ne contient que les items qui ont les attributs indexés », donc les items sans l'attribut de clé ne sont simplement pas propagés (AWS — Take advantage of sparse indexes).
Maintenant la requête lit seulement les items en déstockage, facturée seulement pour eux :
Query ON ClearanceIndex GSI_PK = "CLEARANCE" (sorted by clearanceAt)
Le filtre a eu lieu quand tu as écrit les données — en choisissant de poser ou non
clearanceAt. L'index est l'ensemble filtré. Voir
GSI vs LSI pour quel type d'index convient.
Filtrer avec FilterExpression
« Donne-moi les produits de kitchen en stock. » inStock n'est pas un attribut de
clé, donc tu atteins une FilterExpression :
Query PK = "DEPT#kitchen"
Filter inStock = true
Voici le piège. DynamoDB lit chaque item de la partition kitchen, mesure la
capacité pour tous, puis écarte ceux en rupture de stock.
La règle officielle : une expression de filtre est « appliquée après qu'un Query se
termine, mais avant que les résultats soient renvoyés », et « ne consomme aucune unité
de lecture supplémentaire » — tu as déjà payé la lecture complète
(AWS — Filter expressions for Query).
Donc si kitchen a 10 000 produits et que 12 sont en stock, tu paies pour en lire
10 000. La réponse est petite ; la facture non. FilterExpression réduit la charge
utile qui traverse le fil, jamais la lecture.
Il y a un second tranchant, plus aigu : la pagination est mesuré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 entier, n'a rien matché, 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 des mots réservés — avec le
DynamoDB Expression Builder pour que les
placeholders #inStock/:val soient corrects du premier coup.
Comparer les quatre
| Quand il 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é seule | Gratuit (c'est la clé) |
| Clé de tri | Avant la lecture | Oui — une tranche | Plage / begins_with | Conception de clé de tri |
| Sparse index | Avant la lecture | Oui — index seul | Présence d'un attribut | GSI en plus + coût d'écriture |
| FilterExpression | Après la lecture | Non | Presque toute condition | Aucun |
Lis le tableau de haut en bas : la puissance du prédicat monte, le contrôle du coût
descend. FilterExpression peut exprimer n'importe quoi avec précision parce qu'il
s'exécute sur des items déjà lus — c'est la même raison pour laquelle il ne peut pas te
faire économiser.
Vois-le dans DynoTable
Quand tu exécutes un Query avec un filtre, l'écart entre les items lus et les items
renvoyés est toute l'histoire. DynoTable fait apparaître la capacité consommée à côté
du nombre de résultats — donc un filtre qui lit silencieusement toute la partition est
visible, pas caché dans ta facture mensuelle.
Pour de vraies questions inter-items qu'un filtre ne peut pas répondre — « 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 de table entière.
Pièges et étapes suivantes
- N'utilise pas
FilterExpressioncomme chemin d'accès principal. Si un mode est courant, modélise-le dans une clé ou un sparse index. Un filtre sert au dernier petit bout de restriction, pas au gros. - 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 sparse index 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 capacité, et essaie DynoTable pour observer la capacité consommée face aux lignes renvoyées sur tes propres tables.