La recherche vectorielle DynamoDB
DynamoDB a gagné la recherche vectorielle native le 5 août 2026. Tu stockes les embeddings comme une simple List de valeurs Number sur tes items, tu ajoutes un index vectoriel, et tu lances des requêtes approchées de plus proches voisins avec la nouvelle API SearchVectors.
Jusqu'ici, la recherche par similarité voulait dire répliquer ta table dans OpenSearch ou dans une base vectorielle séparée et garder les deux synchronisées. Ce pipeline a disparu. Le modèle de facturation qui le remplace ne ressemble à rien d'autre dans DynamoDB.
DynamoDB prend-il en charge la recherche vectorielle ?
Oui — nativement, depuis le 5 août 2026. Tu stockes les embeddings comme une
simple List de valeurs Number, tu ajoutes un index vectoriel, et tu
lances des requêtes approchées de plus proches voisins avec l'API
SearchVectors — pas de réplique OpenSearch, pas de base vectorielle séparée.
Ça ne fonctionne que sur les tables à la demande, jusqu'à 4,096 dimensions, et
c'est facturé à l'octet écrit, examiné et stocké.
- Une troisième famille d'index : les index vectoriels prennent place à côté des et des LSI. Une nouvelle API de lecture (
SearchVectors), ANN uniquement, tables à la demande uniquement, jusqu'à 4,096 dimensions. - C'est rapide, et mesuré : face à notre index réel de 1024 dimensions dans us-east-1,
SearchVectorsa répondu aussi vite queGetItemdepuis le même client (p50 39 ms contre 44 ms), et une écriture fraîche est devenue trouvable par la recherche en ~136 ms. - La facturation est à l'octet : $0.52 par Go d'écritures vectorielles, $0.002 par Go de données vectorielles examinées par une recherche, $0.25 par Go-mois de stockage (us-east-1). Indexer un embedding fait facturer sa copie en table de base à exactement 4 octets par dimension ; la même liste non indexée se facture ~1.9×.
- S3 Vectors reste le magasin de masse : environ 8× moins cher au repos et bien moins cher à charger par lots. DynamoDB gagne sur les lectures à la milliseconde, les écritures en continu, et les vecteurs qui vivent à côté de l'item qu'ils décrivent.
Comment fonctionne un index vectoriel
Il n'y a pas de nouveau type d'attribut. Un embedding est une liste ordinaire de nombres sur l'item, {"L": [{"N": "0.0132"}, {"N": "-0.0475"}, …]} sur le fil, écrite avec les mêmes PutItem et UpdateItem que tu utilises déjà.
L'index est une structure séparée. DynamoDB y réplique le vecteur de manière asynchrone en précision flottante 32 bits, avec les attributs que tu projettes ou sur lesquels tu filtres. Les résultats de recherche sont à cohérence à terme, comme une lecture de .
Le décalage est faible en pratique. Face à notre index de test réel, un vecteur fraîchement écrit est apparu dans les résultats de recherche environ 136 ms après le retour du PutItem. Malgré tout, ne construis jamais un flux « lire sa propre écriture » dessus.
Où il se situe à côté des types d'index que tu connais :
| Index vectoriel | GSI | LSI | |
|---|---|---|---|
| Max par table | 5 | 20 | 5 |
| API de lecture | SearchVectors | Query, Scan | Query, Scan |
| PartiQL | Non | Oui | Oui |
| Mode de capacité | À la demande uniquement | Les deux | Les deux |
| Cohérence | À terme | À terme | Forte disponible |
| Ajout après création de la table | Oui | Oui | Non |
Chaque index fige son nombre de dimensions (jusqu'à 4,096) et l'une des trois fonctions de distance à la création. COSINE et EUCLIDEAN notent plus-bas-plus-similaire ; DOT_PRODUCT note plus-haut-plus-similaire et peut devenir négatif. Rien de tout cela ne peut être changé ensuite.
Une note de précision avant de benchmarker quoi que ce soit. L'index détient les vecteurs en f32 ; les valeurs de plus haute précision sont acceptées mais perdent en précision à l'entrée. Si tu arrives avec des embeddings en float64, chaque distance est calculée contre la copie f32, donc mesure le rappel contre f32, pas contre tes originaux.
Crée-en un et interroge-le
Imagine que tu fais de la recherche sémantique sur des tickets de support, pour qu'un agent puisse trouver « des clients qui ont déjà rencontré ce problème » sans correspondance de mots-clés. Chaque item de ticket porte un embedding de son sujet et de son corps, généré par le modèle de ton choix (Titan Text Embeddings V2 coûte $0.02 par million de tokens d'entrée sur Bedrock).
Ajoute l'index à la table existante. L'élément HASH restreint chaque recherche à une seule valeur de product ; les attributs INLINE_FILTER (jusqu'à 18) permettent des filtres d'égalité au moment de la recherche :
aws dynamodb update-table \
--table-name SupportTickets \
--attribute-definitions AttributeName=product,AttributeType=S \
AttributeName=severity,AttributeType=S \
--vector-index-updates '[{"Create": {
"IndexName": "TicketEmbeddings",
"VectorAttribute": {"AttributeName": "embedding"},
"SearchSchema": [
{"AttributeName": "product", "SearchSchemaElementType": "HASH"},
{"AttributeName": "severity", "SearchSchemaElementType": "INLINE_FILTER"}
],
"Projection": {"ProjectionType": "KEYS_ONLY"},
"Dimensions": 1024,
"DistanceFunction": "COSINE"
}}]'La construction se comporte comme un backfill de GSI, avec des angles plus tranchants. SearchVectors renvoie ValidationException pendant toute la construction, sans résultats partiels.
AWS prévient que l'endpoint de recherche peut continuer à rejeter un moment, même après que DescribeTable dit ACTIVE. Il n'y a pas de waiter ; sonde avec une vraie recherche dans une boucle de retry. Quand nous avons créé l'index en même temps qu'une table vide, il est passé ACTIVE en 26 secondes et a accepté les recherches 0.6 s plus tard.
La recherche prend l'embedding de requête comme un simple tableau JSON de valeurs {"N": …}. Ne l'enveloppe pas dans un L DynamoDB. L'attribut stocké utilise le type liste, le paramètre de requête non, et les confondre est une première erreur facile :
aws dynamodb search-vectors \
--table-name SupportTickets \
--index-name TicketEmbeddings \
--search-vector file://query-embedding.json \
--top-k 5 \
--search-condition-expression "product = :p AND severity = :sev" \
--expression-attribute-values '{":p": {"S": "checkout"}, ":sev": {"S": "high"}}'Tu récupères jusqu'à TopK items triés du plus similaire au moins similaire, chacun avec un Score, plus ConsumedCapacity quand tu le demandes. TopK plafonne à 100, il n'y a pas de pagination, et la réponse plafonne à 16 Mo.
L'embedding lui-même est exclu des résultats, sauf si tu le projettes et le demandes. Ce défaut est délibéré ; renvoyer des vecteurs gonfle à la fois la réponse et la facture de recherche.
Les expressions de filtre n'acceptent que l'égalité, sans BETWEEN, IN ni begins_with. Quand l'index définit un attribut HASH, chaque recherche doit épingler exactement une valeur pour lui. La formulation d'AWS sur les opérateurs de plage est « not yet available », donc cela pourrait s'assouplir.
Deux surprises opérationnelles valent d'être connues avant le premier déploiement. SearchVectors a besoin de la nouvelle action IAM dynamodb:SearchVectors, qu'aucune de tes politiques de lecture existantes n'inclut.
Elle parle aussi à un endpoint séparé, search-dynamodb.{region}.amazonaws.com. Les listes d'autorisation de sortie et les configurations de VPC endpoint qui ne couvrent que dynamodb.{region} cassent la recherche vectorielle seule, avec une erreur de connexion qui ne dit jamais pourquoi.
Ce qu'un index vectoriel facture
Trois nouveaux compteurs, tous à l'octet, avec un minimum de 1 Ko par écriture et par requête de recherche, en plus des frais normaux de la table (us-east-1, depuis l'API de tarification AWS, 2026-08-15) :
| Compteur | Standard | Standard-IA |
|---|---|---|
| Écritures vectorielles | $0.52/GB | $0.65/GB |
| Données vectorielles examinées par recherche | $0.002/GB | $0.0025/GB |
| Stockage (table et index) | $0.25/GB-mo | $0.10/GB-mo |
La doc prévient que la copie en table de base d'un embedding, stockée en chaînes décimales à l'intérieur d'une List, peut être « considerably larger » que la copie f32 de l'index. Nous avons mesuré la facturation en unités d'écriture sur des tables réelles dans us-east-1, et la vérité est plus étrange.
Un embedding sur un attribut sans index vectoriel se facture selon la règle décimale documentée, environ 1.9× la taille f32. Pointe un index vectoriel sur ce même attribut et sa facturation en table de base tombe à exactement 4 octets par dimension :
| Dimensions | Attribut List non indexé (facturé) | Même attribut, indexé vectoriellement (facturé) |
|---|---|---|
| 256 | 1,914 B | 1,024 B |
| 768 | 5,760 B | 3,072 B |
| 1,024 | 7,653 B | 4,096 B |
| 1,536 | 11,501 B | 6,144 B |
| 3,072 | 22,957 B | 12,288 B |
Mesuré en cherchant par dichotomie la frontière d'unité d'écriture avec un attribut de remplissage, clé d'item fraîche à chaque écriture, calibré à l'octet près. Notre item de ticket complet à 1024 dimensions a facturé 5 unités d'écriture ; l'item identique sans index sur embedding en facture 8.
Le compteur d'écritures vectorielles a suivi de près la taille f32 dans les mêmes runs. VectorWriteRequestBytes est revenu à 4 octets par dimension plus 11 B de surcoût de clé sur un index nu, et plus 65 B avec notre schéma de recherche à deux attributs.
La facturation de la recherche est le compteur que tu ne peux pas calculer à l'avance. VectorSearchRequestBytes suit la quantité de données vectorielles que la traversée ANN a examinée, et la recommandation d'AWS elle-même est de la mesurer via ReturnConsumedCapacity plutôt que de l'estimer à partir du nombre de dimensions.
Notre sonde donne les premiers points de données. TopK=10 sur une partition de 50 vecteurs a examiné 22.2-22.4 Ko par recherche ; la même recherche sur une partition de 1 vecteur a quand même examiné 21.4 Ko, donc à petite échelle il existe un plancher d'environ 21 Ko (à peu près $0.00000004) par requête. Le tutoriel d'AWS rapporte 31,449 octets pour son propre exemple à 50 vecteurs.
Modélise les coûts côté table dans le calculateur de tarifs ; les compteurs vectoriels s'empilent sur les unités d'écriture qu'il calcule déjà.
Recherche vectorielle DynamoDB vs S3 Vectors
AWS vend désormais deux magasins vectoriels serverless, et ils sont construits pour des modes d'accès opposés. S3 Vectors (GA en décembre 2025) contient jusqu'à 2 milliards de vecteurs par index à $0.06 par Go-mois, répond dans la plage de 100 ms à 1 s, et facture chaque requête sur la taille de l'index entier.
DynamoDB répond en millisecondes et facture sur ce que la recherche examine, pas sur ce que l'index contient.
| Recherche vectorielle DynamoDB | S3 Vectors | |
|---|---|---|
| GA | Août 2026 | Déc. 2025 |
| Classe de latence | ms à un chiffre (revendication AWS) | ~100 ms fréquent, sous 1 s peu fréquent (revendication AWS) |
| Plafond d'échelle | Pas de plafond de vecteurs annoncé ; plafond de table de 600 Go pour la création d'index (souple) | 2 milliards de vecteurs par index |
| Dimensions max | 4,096 | 4,096 |
| Fonctions de distance | Cosinus, euclidienne, produit scalaire | Cosinus, euclidienne |
| Écritures d'index | Async depuis la table (cohérence à terme) | Fortement cohérentes |
| Filtrage | Égalité uniquement, ≤18 attributs + 1 clé de partition | Filtres de métadonnées riches, plafond filtrable de 2 Ko par vecteur |
| TopK | 100, pas de pagination | 10,000, paginé |
| Stockage | $0.25/GB-mo, deux fois (table + index) | $0.06/GB-mo, une fois |
| Écritures | $0.52/GB, min 1 Ko/requête | $0.20/GB, min 128 Ko/PUT |
| Requêtes | $0.002/GB examiné | $2.50/M requêtes + frais d'octets traités sur l'index entier |
Les minimums d'écriture décident du cas du streaming, et ils pointent dans la direction opposée aux tarifs de stockage. En écrivant un vecteur de 1024 dimensions à la fois, par million d'écritures (à partir des tarifs vérifiés et de nos 5 unités d'écriture + 4,161 octets d'écriture vectorielle mesurés par item) :
| Mode d'écriture | DynamoDB | S3 Vectors |
|---|---|---|
| Écritures vecteur par vecteur | ~$5.14/M | ~$24.41/M |
Par lots (500 par PutVectors) | n/a (les écritures sont par item) | ~$0.78/M |
Le minimum de 128 Ko par PUT de S3 en fait l'option chère pour exactement la charge de travail que l'on suppose bon marché chez lui. Envoie les vecteurs un par un dans S3 Vectors et tu paies presque 5× le tarif de DynamoDB ; charge-les par lots et tu paies environ 7× moins.
Totaux mensuels de stockage et de requêtes pour un corpus de 1024 dimensions à 1 M de requêtes par mois, calculés à partir des tarifs vérifiés (les coûts d'écriture sont le tableau par million ci-dessus). Les frais de requête de S3 Vectors suivent sa formule publiée, la taille de l'index entier multipliée par un tarif par paliers.
Les frais de requête de DynamoDB dépendent des octets examinés, donc nous montrons une plage de sensibilité au lieu de prétendre connaître ta traversée :
| Corpus | Stockage DynamoDB | Requêtes DynamoDB (4 / 40 / 400 Mo examinés) | Stockage S3 Vectors | Requêtes S3 Vectors |
|---|---|---|---|---|
| 1 M de vecteurs | ~$1.95 | $8 / $80 / $800 | ~$0.23 | ~$11 |
| 10 M de vecteurs | ~$19.50 | $8 / $80 / $800 | ~$2.35 | ~$80 |
| 100 M de vecteurs | ~$195 | $8 / $80 / $800 | ~$23.50 | ~$217 |
Deux choses ressortent de ce tableau. Le coût par requête de DynamoDB ne croît pas avec la taille du corpus — une recherche ANN examine un voisinage, pas l'index, et la restriction par clé de partition le réduit encore.
L'avantage de stockage de S3 Vectors (~8×, puisque DynamoDB stocke deux copies f32 à 4× le tarif) se cumule pour toujours, que quelqu'un requête ou non.
Quand utiliser lequel
- Les vecteurs décrivent des items vivants que tu gardes déjà dans DynamoDB (tickets, produits, sessions utilisateur, mémoire d'agent) : utilise l'index vectoriel. Un seul chemin d'écriture, un seul item, pas de pipeline de synchronisation qui dérive.
- Des millions d'embeddings, requêtés occasionnellement (RAG sur des documents, archives, jobs nocturnes) : utilise S3 Vectors. Charge par lots à bas coût, paie $0.06 par Go au repos, tolère quelques centaines de ms.
- Un QPS élevé avec classement hybride (pertinence textuelle + vecteurs, facettes, agrégations) : OpenSearch reste la réponse, avec un plancher d'infrastructure d'environ $350 par mois pour une collection serverless classique.
- Des vecteurs joints à des données relationnelles : Aurora PostgreSQL avec pgvector, qui descend à zéro et atterrit sous ~$50 par mois pour de petites charges RAG.
Le choix honnête par défaut pour une équipe qui tourne sur DynamoDB, c'est les deux. Garde les vecteurs chauds et filtrables sur la table, où les écritures sont atomiques avec l'item, et archive la longue traîne dans S3 Vectors, dont les écritures par lots fortement cohérentes en font un réceptacle propre.
Les pièges
- Désindexation silencieuse : un item auquel manque l'attribut
HASHde l'index s'écrit très bien dans la table et n'entre jamais dans l'index vectoriel. Nous l'avons reproduit en conditions réelles : lePutItema réussi, et le vecteur était absent de chaque partition 15 secondes plus tard. Pas d'erreur, pas de résultat, rien dans la réponse pour te prévenir. - Les écritures à la mauvaise dimension sont rejetées : change de modèle d'embedding sans migrer et chaque écriture échoue avec une
ValidationExceptionqui nomme l'attribut et les deux tailles (Invalid size for parameter, capturée verbatim sur sa propre page), puisque l'index fige le nombre de dimensions pour toujours. - Embeddings périmés : DynamoDB ne recalcule jamais les vecteurs. Modifie le texte d'un ticket sans réécrire
embeddinget les recherches correspondent silencieusement à l'ancien contenu. Streams plus un consommateur de régénération est le correctif standard. TopKrenvoie toujours K items : avec trois bonnes correspondances et--top-k 10, tu en reçois quand même 10. Juge la pertinence auScore, pas au nombre de résultats, et souviens-toi que le sens du score s'inverse entre les fonctions de distance.- Les minimums de 1 Ko : les vecteurs de faible dimension ne sont pas facturés proportionnellement moins cher, ni en écriture ni en recherche.
- Tout est immuable : les dimensions, la fonction de distance et l'ensemble d'attributs d'une projection
INCLUDEexigent tous un supprimer-et-recréer pour changer. Le stockage de l'index se facture pendant toute la vie de l'index, requêté ou non.
Essaie-la sur tes propres tables
La recherche vectorielle hérite de la discipline de coûts que le reste de DynamoDB t'a apprise. Dimensionne l'embedding avant de t'y engager, parce que les limites de taille d'item s'appliquent toujours et qu'un embedding de 3072 dimensions ajoute 12 Ko à chaque écriture d'item sur les deux compteurs.
La mécanique de l'index te semblera familière si tu sais comment les GSI se répliquent de manière asynchrone et quand choisir un GSI plutôt qu'un LSI. Pour la recherche lexicale sur les mêmes données, DynamoDB n'a toujours pas de moteur plein texte ; la recherche vectorielle fait correspondre le sens plutôt que l'orthographe.
Vérifie le coût réel en octets d'un embedding dans le calculateur de taille d'item, puis essaie DynoTable pour parcourir les items derrière ton index vectoriel — les embeddings s'affichent comme des attributs de liste ordinaires, juste à côté des champs sur lesquels tu filtres.