Intermédiaire15 min de lecture

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, SearchVectors a répondu aussi vite que GetItem depuis 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.

réplication async, f32PutItem / UpdateItemTable de baseembedding en List de NumberIndex vectorielvecteurs + attributs de filtreSearchVectorsTopK + filtres d'égalitéTop-K items avec scores

Où il se situe à côté des types d'index que tu connais :

Index vectorielGSILSI
Max par table5205
API de lectureSearchVectorsQuery, ScanQuery, Scan
PartiQLNonOuiOui
Mode de capacitéÀ la demande uniquementLes deuxLes deux
CohérenceÀ termeÀ termeForte disponible
Ajout après création de la tableOuiOuiNon

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) :

CompteurStandardStandard-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 :

DimensionsAttribut List non indexé (facturé)Même attribut, indexé vectoriellement (facturé)
2561,914 B1,024 B
7685,760 B3,072 B
1,0247,653 B4,096 B
1,53611,501 B6,144 B
3,07222,957 B12,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 DynamoDBS3 Vectors
GAAoût 2026Déc. 2025
Classe de latencems à un chiffre (revendication AWS)~100 ms fréquent, sous 1 s peu fréquent (revendication AWS)
Plafond d'échellePas 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 max4,0964,096
Fonctions de distanceCosinus, euclidienne, produit scalaireCosinus, euclidienne
Écritures d'indexAsync depuis la table (cohérence à terme)Fortement cohérentes
FiltrageÉgalité uniquement, ≤18 attributs + 1 clé de partitionFiltres de métadonnées riches, plafond filtrable de 2 Ko par vecteur
TopK100, pas de pagination10,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'écritureDynamoDBS3 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 :

CorpusStockage DynamoDBRequêtes DynamoDB (4 / 40 / 400 Mo examinés)Stockage S3 VectorsRequê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 HASH de 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 : le PutItem a 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 ValidationException qui 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 embedding et les recherches correspondent silencieusement à l'ancien contenu. Streams plus un consommateur de régénération est le correctif standard.
  • TopK renvoie toujours K items : avec trois bonnes correspondances et --top-k 10, tu en reçois quand même 10. Juge la pertinence au Score, 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 INCLUDE exigent 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.

Mis à jour