Pourquoi un Scan DynamoDB est lent et coûteux
Un Scan lit chaque item de la table et ne filtre qu'ensuite. C'est
l'opération que tu dégaines par réflexe hérité de SQL, et celle qui fait
grimper ta facture en silence tout en rendant ta latence pire que celle de la machine RDS que tu as quittée.
Pourquoi mon Scan DynamoDB est-il lent et coûteux ?
Un Scan lit chaque item de la table avant que la FilterExpression ne s'exécute, donc
tu paies pour lire la table entière quel que soit le nombre de lignes renvoyées, et il devient
plus lent à mesure que la table grandit. Le correctif est presque toujours une Query par clé — modélise le
mode d'accès autour d'une clé pour que DynamoDB touche une seule partition au lieu de
tout.
- Un
Scanlit toute la table, à chaque fois. C'est la taille, pas ton nombre de résultats, qui décide de ce que tu paies et du temps que ça prend. - La
FilterExpressionest un mensonge sur le coût. Elle s'exécute après que la lecture est comptabilisée, donc renvoyer 12 items peut te facturer la lecture de 12 millions. - Un
Scandevient plus lent à mesure que tu grandis. UneQuerypar clé reste stable — elle touche une seule partition quelle que soit la taille de la table. - Le correctif est presque toujours la modélisation, pas le réglage. Si tu fais un
Scanpour répondre à une question de routine, il te manque une clé.
Ce qu'un Scan fait réellement
En venant de SQL, SELECT * FROM events WHERE type = 'checkout' semble gratuit —
le moteur a un index, ou il n'en a pas, mais dans les deux cas tu récupères des lignes. Dans
DynamoDB il n'y a pas de planificateur de requêtes qui décide ça pour toi.
Un Scan parcourt toute la table séquentiellement, 1 Mo à la fois, et remet chaque
page à ta FilterExpression. Tout ce que le filtre rejette est quand même lu,
quand même comptabilisé, et quand même sur ta facture. (AWS : parcourir des tables)
C'est le piège. Le filtre ressemble à une clause WHERE, mais il change le
jeu de résultats, jamais le coût. Un Scan consomme la même capacité de lecture qu'un
filtre soit présent ou non. (AWS : parcourir des tables)
Compte les unités de lecture
DynamoDB comptabilise les lectures en (RCU). Une RCU achète une seule lecture d'un item jusqu'à 4 Ko ; les lectures à coûtent la moitié. Les items plus gros sont arrondis au multiple de 4 Ko supérieur. (AWS : modes de capacité de lecture/écriture)
Prends une table analytique, ProductEvents. Chaque ligne est un événement suivi :
PK = "TENANT#acme"
SK = "TS#2026-06-23T14:08:55Z#evt_9f3a"
attrs: eventType, sessionId, userId, payloadBytesDisons qu'elle contient 2 000 000 d'événements, chacun d'environ 1 Ko, tous sous un même locataire actif. Tu veux les checkouts d'aujourd'hui. Le geste réflexe :
Scan ProductEvents
FilterExpression: eventType = "checkout"
Ce filtre pourrait renvoyer 40 lignes. Mais le Scan a d'abord lu les 2 000 000 d'items.
À environ 1 Ko chacun (1 RCU par 4 Ko, cohérence à terme ≈ 0,5 RCU par 4 Ko),
tu as comptabilisé environ 250 000 RCU — et parcouru environ 2 Go de données — pour
te rendre 40 items.
Maintenant modélise le mode d'accès comme une clé et fais plutôt une Query dessus :
Query ProductEvents
PK = "TENANT#acme"
AND SK begins_with "TS#2026-06-23"
Cela ne lit que la tranche correspondante d'une seule partition. Si ces 40 lignes de checkout plus les autres événements du jour totalisent environ 2 Mo, tu paies pour environ 2 Mo de lectures, pas 2 Go. Même réponse, une infime fraction du coût — et la latence reste stable à mesure que la table grandit.
Scan vs Query, comptabilisés
| Scan + filtre | Query par clé | |
|---|---|---|
| Lectures | Chaque item de la table | Une partition, réduite par la SK |
| Capacité facturée | Toute la table, avant le filtre | Seulement les items de ta tranche |
| Notre exemple | ~250 000 RCU (~2 Go) | quelques centaines de RCU (~2 Mo) |
| Latence | Croît avec la taille de la table | Stable à mesure que la table grandit |
| Nombre de résultats | Ne décide rien sur le coût | Correspond à ce que tu paies |
La leçon que la table encode : sur un Scan, ton nombre de résultats et ta facture sont
sans rapport. Sur une Query, ils vont de pair.
Décide avant de faire un Scan
La plupart des Scan accidentels viennent d'une seule question : puis-je nommer la partition dont
j'ai besoin ? Si oui, c'est une Query. Si non, le correctif est une clé, pas un filtre plus gros.
Voici la décision sous forme de flux.
Le chemin se termine presque toujours à Query ; tu ne retombes sur Scan que lorsqu'aucune
clé — présente ou ajoutable — ne convient au mode d'accès.
Si le mode d'accès est réel et récurrent mais que la table de base ne peut pas le clé, c'est
le signal d'ajouter un Global Secondary Index pour que la question
devienne une Query. Modéliser tes clés autour de tes modes d'accès dès le départ est
tout l'enjeu — voir single-table design.
Écris la query par clé, pas un filtre
Quand tu as besoin d'une condition au-delà de la clé, construis-la délibérément plutôt que
de tout déverser dans une FilterExpression. Le
Générateur d'expressions DynamoDB génère la
KeyConditionExpression et les placeholders d'attributs pour toi, pour que la clé de partition
et la clé de tri fassent la réduction — avant que DynamoDB ne comptabilise la lecture, pas après.
KeyConditionExpression: PK = :tenant AND begins_with(SK, :day)
Quand un Scan est réellement acceptable
Un Scan n'est pas interdit — c'est simplement le mauvais choix par défaut. C'est le bon outil quand
tu veux vraiment dire « tout lire » :
- Exports ponctuels ou remplissages lancés à la main.
- Petites tables de config / de correspondance où toute la table fait quelques Ko.
- Tâches d'arrière-plan qui paginent volontairement la table entière. Répartis-les entre des
workers avec
Segment/TotalSegments— un — au lieu d'un seul long parcours séquentiel. (AWS : parcourir des tables)
Et note que PartiQL ne te sauve pas : SELECT * FROM ProductEvents WHERE eventType = 'checkout' sans prédicat de clé se compile directement en un Scan.
C'est le même piège en habits SQL. (Voir Query vs Scan
pour l'analyse complète.)
Quand tu as vraiment besoin d'analytique inter-items — un GROUP BY, un JOIN, un agrégat
que DynamoDB ne sait pas exprimer — le SQL Workbench de DynoTable les exécute côté client sur un
jeu de résultats borné, au lieu de marteler la table avec un Scan complet.
Étapes suivantes
Estime ce que coûte chacun des modes avec le calculateur de tarification, lis Query vs Scan pour le contraste au niveau de l'API, et télécharge DynoTable pour les exécuter sur tes propres tables et observer combien d'items chaque approche lit réellement.