Intermédiaire8 min de lecture

Les sparse indexes DynamoDB

Un sparse index est un index secondaire qui ne contient que les items portant son attribut de clé — de sorte qu'un petit sous-ensemble chaud d'une table énorme devient sa propre collection pré-filtrée et prête à interroger.

Tu as des millions de lignes mais la requête que tu lances toute la journée ne touche qu'une tranche minuscule : les tickets de support ouverts, les factures impayées, les comptes signalés pour examen.

Filtrer cette tranche scanne quand même toute la table et te facture chaque lecture. Un sparse index rend l'index lui-même petit à la place.

Qu'est-ce qu'un sparse index dans DynamoDB ?

Un sparse index est un index secondaire qui ne contient que les items portant son attribut de clé. Parce que DynamoDB ignore tout item qui n'a pas cette clé, tu inventes une clé que seuls les items voulus écrivent — tickets ouverts, factures impayées — et l'index devient exactement ce sous-ensemble. Les requêtes ne lisent alors que lui, sans filtre, sans capacité de lecture gaspillée.

  • Un index secondaire n'indexe que les items qui ont sa clé. Omets la clé sur un item et il n'entre jamais dans l'index — pas de placeholder, pas de ligne nulle.
  • Donc tu inventes une clé que seuls les items voulus portent. Écris-la sur les items que tu interroges, retire-la sur les autres. L'index devient exactement ce sous-ensemble.
  • La requête ne lit que le sous-ensemble, sans filtre. Sa taille suit le petit ensemble chaud, pas le total de la table.
  • REMOVE est le levier, pas le vidage. Une chaîne vide est quand même une valeur et reste indexée — tu dois supprimer l'attribut.

Le problème : filtrer n'économise pas de lectures

Venant de SQL, tu supposes qu'une clause WHERE réduit le travail. Le FilterExpression de DynamoDB ne le fait pas. Il s'exécute après que les items sont lus, pas avant.

D'après le AWS Developer Guide, filtrer « ne réduit pas la quantité de capacité de lecture consommée » — tu paies pour chaque item examiné, puis tu jettes ceux qui ne correspondent pas.

Donc si 50 de tes 5 millions de tickets sont ouverts, un Query/Scan filtré lit des millions pour t'en remettre 50.

C'est l'arme à fragmentation derrière chaque fil « pourquoi mon scan est-il si coûteux » ; query vs. scan a le tableau de coûts complet.

Un sparse index la contourne en rendant l'index lui-même petit.

Comment fonctionne la parcimonie

Un index secondaire n'indexe que les items qui ont réellement les attributs de clé de l'index.

La doc AWS sur les global secondary indexes le dit clairement : « un global secondary index ne contient que les items qui ont les attributs de clé définis pour cet index ».

Manque la clé de partition (ou de tri) du GSI sur un item et DynamoDB ne l'écrit tout simplement pas dans l'index. Pas de placeholder, pas de ligne nulle — l'item est absent.

Cette « absence par défaut » est toute l'astuce. N'indexe pas un attribut status que chaque item porte. Invente un attribut que seuls les items que tu veux interroger portent.

L'index devient alors une liste nette de ces items précis, et un Query contre lui ne lit qu'eux — pas de filtre, pas de capacité gaspillée.

Imagine la table de base alimentant l'index, où seuls les items portant la clé passent de l'autre côté :

Sparse GSI (ouverts seulement)Table de base (tous les items)clé retiréeclé retiréeOuvert : a la cléOuvert : a la cléFermé : pas de cléFermé : pas de cléOuvertOuvert

Seuls les items clés (ouverts) se répliquent vers l'index ; les items fermés n'y entrent jamais.

C'est le même état d'esprit de façonnage des clés que le single-table design : les clés sont des outils que tu construis pour un motif d'accès précis, pas des miroirs fidèles de tes données.

Un exemple travaillé : « les tickets ouverts seulement »

Prends une table de tickets de support. La table de base est clée pour récupérer un ticket par id et lister les tickets d'un client :

PKSKattributes
TICKET#a91fDETAILsubject, body, priority, openState
CUSTOMER#88TICKET#a91fsubject, priority, openState

Sur la durée de vie de la table, la plupart des tickets finissent fermés. Mais la requête de dashboard que tes agents frappent toute la journée est « montre-moi chaque ticket ouvert, du plus ancien au plus récent » — quelques centaines de lignes cachées dans des millions.

Le coup du sparse index : définis un GSI avec la clé de partition openBucket et la clé de tri openedAt, et n'écris openBucket que sur les tickets ouverts. Pose-le à la création du ticket ; fais REMOVE quand le ticket est résolu.

PKSKopenBucketopenedAt
TICKET#a91fDETAILOPEN2026-06-23T09:14:00Z← ouvert : dans l'index
TICKET#b02cDETAILOPEN2026-06-22T16:40:00Z← ouvert : dans l'index
TICKET#77deDETAIL(absent)2026-05-30T11:02:00Z← fermé : PAS dans l'index

Les tickets a91f et b02c portent openBucket, donc ils vivent dans le GSI. Le ticket 77de a été résolu et a eu openBucket retiré, donc il a discrètement disparu. Le dashboard est maintenant une requête bon marché :

Query  IndexName = "open-tickets-index"
KeyConditionExpression: openBucket = "OPEN"
ScanIndexForward: true        # oldest first

Ça ne lit que les tickets ouverts. À mesure que les tickets ferment, l'index rétrécit de lui-même — sa taille suit la population ouverte, jamais le total.

Une seule valeur de partition statique ("OPEN") convient ici précisément parce que l'ensemble reste petit. Un énorme ensemble ouvert nécessiterait une clé de partition découpée (sharded), mais l'index « petit sous-ensemble » est exactement l'endroit où une seule valeur est le bon choix.

La transition qui fait fonctionner le tout est une seule update expression — supprimer l'attribut quand le ticket est résolu.

Prototype cette clause REMOVE et la condition de clé typée pour le côté lecture dans le DynamoDB Expression Builder, au lieu d'assembler à la main toi-même les ExpressionAttributeNames et les placeholders :val.

Fais-le dans DynoTable

Le plus dur dans un sparse index n'est pas la lecture — c'est de voir quels items sont entrés dans l'index et lesquels sont discrètement tombés.

DynoTable te laisse basculer une vue table sur un index secondaire et voir exactement le sous-ensemble peuplé. Tu peux ainsi confirmer qu'un ticket résolu a vraiment quitté open-tickets-index au lieu de traîner avec une clé périmée.

La table de tickets de support vue à travers son sparse GSI tickets-ouverts dans DynoTable, ne montrant que les items qui portent la clé openBucket.
La table de tickets de support vue à travers son sparse GSI tickets-ouverts dans DynoTable, ne montrant que les items qui portent la clé openBucket.

Pièges et étapes suivantes

Quelques points à surveiller :

  • Retire la clé, ne la vide pas. Une chaîne vide est quand même une valeur, et DynamoDB indexera un item dont openBucket vaut "". Pour retirer un item de l'index tu dois faire REMOVE sur l'attribut — le mettre à une valeur falsy le garde dedans.
  • L'index est à cohérence à terme. Les GSI se mettent à jour de façon asynchrone, donc un ticket tout juste résolu peut brièvement apparaître encore — les lectures de GSI ne supportent que la cohérence à terme. Ne t'y fie pas pour « ce ticket est-il ouvert en ce moment ».
  • Attention aux attributs projetés. Un Query sur l'index ne renvoie que les attributs qui y sont projetés. Si le dashboard a besoin du sujet et de la priorité, projette-les — ou paie un GetItem supplémentaire pour l'item de base complet.
  • C'est une force du GSI, pas du LSI. Les local secondary indexes partagent la clé de partition de la table de base et ne peuvent pas exclure sélectivement des items de cette façon. GSI vs. LSI décortique le compromis.

Les sparse indexes sont l'une des plus vieilles idées du modèle. Le papier Amazon Dynamo de 2007 original a bâti le store autour du service à bas coût de motifs d'accès connus et à fort volume.

Un sparse index est exactement ça : façonne les clés pour que la requête courante ne lise rien dont elle n'a pas besoin.

Pour en construire et inspecter un pour de vrai, télécharge DynoTable, pointe-le vers ta table, et bascule la vue données vers ton sparse GSI — regarde le sous-ensemble se mettre à jour à mesure que les items gagnent et perdent la clé de l'index.

Mis à jour