Intermédiaire8 min de lecture

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é.
  • FilterExpression est 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).

OuiNonTable de base tous les produitsA clearanceAt ?Répliqué vers ClearanceIndexPas dans l'indexQuery l'index = items endéstockage seulement

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 filtreRéduit le coût de lecture ?Puissance du prédicatCoût de mise en place
Clé de partitionAvant la lectureOui — une partitionÉgalité seuleGratuit (c'est la clé)
Clé de triAvant la lectureOui — une tranchePlage / begins_withConception de clé de tri
Sparse indexAvant la lectureOui — index seulPrésence d'un attributGSI en plus + coût d'écriture
FilterExpressionAprès la lectureNonPresque toute conditionAucun

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 FilterExpression comme 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.

Mis à jour