Amazon DynamoDBのベクトル検索は、テーブルに保存した埋め込みベクトルへ「ベクトルインデックス」を張り、SearchVectors APIで類似アイテムを取り出す機能です。2026年8月5日に一般提供となり、別のベクトルデータベースへ複製しなくても、業務データの隣で意味検索やRAGの検索部分を動かせるようになりました。この記事では、AWS CLIでの作成から検索までの手順、書き込み・検索・ストレージの3軸で決まる料金、本番で詰まる落とし穴、S3 Vectorsとの使い分けを順に整理します。
まとめ:DynamoDBのベクトル検索を選ぶ条件と避ける場面
正本データがすでにDynamoDBにあり、検索対象をテナントやカテゴリ単位に区切れるなら、DynamoDBのベクトル検索を第一候補にしてよい。複製パイプラインが要らず、TTLや削除がそのままインデックスに反映されるからです。
見送るのは3つの場面です。範囲条件のフィルタ(価格帯・日付)が検索の中心にある場合、1回で100件を超える候補が必要な場合、そしてテナント間の分離をIAMで強制したい場合。最初の2つはS3 VectorsやOpenSearchの領分で、3つ目はテーブルかインデックスをテナントごとに分ける設計が要ります。
DynamoDBのベクトル検索の仕組みとGSI・LSIとの違いを押さえる
ベクトルインデックスは、GSI・LSIと並ぶ3種類目のインデックスです。DynamoDB自体の基本(パーティションキー設計や容量モード)はAmazon DynamoDBとは:機能の全体像からローカル起動・キー設計・容量モード選定までで扱っているので、ここではベクトル検索に固有の部分だけを見ます。
ベクトルインデックスとSearchVectors APIの関係とANN検索の位置づけ
公式のDeveloper Guideによると、ベクトルインデックスは近似最近傍(ANN)探索で、クエリベクトルに近いアイテムをスコア順に返します。読み取りは専用のSearchVectorsだけで、Query・Scan・PartiQLからは読めません。作成と削除は既存のCreateTable・UpdateTableに追加されたVectorIndexes/VectorIndexUpdatesパラメータで行います。
埋め込みの生成はDynamoDBの外の仕事です。Amazon Titan Text EmbeddingsやCohere Embedなどで作ったベクトルを、数値のList型属性として保存します。GA告知のAWS News Blogは1桁ミリ秒のレイテンシと99%以上のリコールをうたっています。ベクトル検索そのものの考え方はベクトル検索とセマンティック検索の違いを先に読むと早い。
GSI・LSIとの違いを表で比較:上限5個とオンデマンド専用の制約
公式の比較表をもとに、設計に効く差だけを抜き出しました。
| 項目 | ベクトルインデックス | GSI | LSI |
|---|---|---|---|
| 検索の種類 | 類似度検索 | 完全一致・範囲 | 完全一致・範囲 |
| 読み取りAPI | SearchVectors | Query・Scan | Query・Scan |
| テーブルあたり上限 | 5 | 20 | 5 |
| 容量モード | オンデマンドのみ | 両方 | 両方 |
見落としやすいのは容量モードです。ベクトルインデックスを付けるテーブル自体もオンデマンド(PAY_PER_REQUEST)でなければならず、プロビジョンドで予約キャパシティを買っているテーブルには追加できません。上限5個は申請で引き上げられますが、作成と削除は1テーブルにつき同時に1つだけで、この枠はGSIの作成とも共有されます。
距離関数COSINE・EUCLIDEAN・DOT_PRODUCTの選び方とスコアの読み方
距離関数は作成後に変えられません。テキスト埋め込みならCOSINEが無難で、公式も迷ったときの既定として挙げています。COSINEとEUCLIDEANはスコアが小さいほど近く、DOT_PRODUCTは大きいほど近い。並べ替えの向きが逆になる点はアプリ側のしきい値処理で効いてきます。
公式の例では、クエリ[1, 0, 0, 0]に対し[10, 0, 0, 0]はCOSINEで0.0(同一扱い)、EUCLIDEANで9.0(最下位)でした。DOT_PRODUCTは負の値も返します。「スコアは0以上」という前提でフィルタを書くと結果が欠けるので注意してください。
AWS CLIでベクトルインデックスを作りSearchVectorsで検索する手順
ここからは公式の作成・検索手順に沿って、商品説明の埋め込みを1536次元で保存し、カテゴリ単位で検索する構成を組みます。ベクトル検索に対応した版のAWS CLIが前提です。
create-tableでベクトルインデックス付きテーブルを作成する設定例
新規テーブルなら--vector-indexesで同時に作れます。Categoryをインデックスのパーティションキー(HASH)、Brandをインラインフィルタにした例です。
aws dynamodb create-table \
--table-name Products \
--attribute-definitions AttributeName=ProductId,AttributeType=S \
AttributeName=Category,AttributeType=S AttributeName=Brand,AttributeType=S \
--key-schema AttributeName=ProductId,KeyType=HASH \
--billing-mode PAY_PER_REQUEST \
--vector-indexes '[{
"IndexName": "ProductEmbeddingIndex",
"VectorAttribute": {"AttributeName": "Embedding"},
"SearchSchema": [
{"AttributeName": "Category", "SearchSchemaElementType": "HASH"},
{"AttributeName": "Brand", "SearchSchemaElementType": "INLINE_FILTER"}],
"Projection": {"ProjectionType": "ALL"},
"Dimensions": 1536,
"DistanceFunction": "COSINE"}]'
SearchSchemaで使う属性は、GSIのキーと同じく--attribute-definitionsにも宣言が要ります。Dimensionsは埋め込みモデルの出力次元に合わせ、上限は4,096です。既存テーブルへ追加する場合はupdate-table --vector-index-updatesにCreateを渡します。
埋め込みをList型で書き込むput-itemと次元数不一致で拒否される条件
書き込みは通常のPutItem・UpdateItem・BatchWriteItem・TransactWriteItemsです。ベクトルはL型の中にN要素を並べます。
{
"ProductId": {"S": "prod-123"},
"Category": {"S": "Electronics"},
"Brand": {"S": "Acme"},
"Title": {"S": "Wireless Headphones"},
"Embedding": {"L": [{"N": "0.1234"}, {"N": "-0.5678"}, {"N": "0.9012"}]}
}
上のEmbeddingは3要素に省略しています。実際には1,536個並べないと書き込み自体が拒否されます。インデックス側の精度は32ビット浮動小数点で、それより細かい値は受け付けられても索引上では丸められる仕様です。保存はaws dynamodb put-item --table-name Products --item file://item.jsonで行います。
search-vectorsでTopK件を取得しパーティションキーで範囲を絞る方法
クエリベクトルはL型で包まず、[{"N": "0.1234"}, ...]という素の配列をファイルに置きます。インデックスにパーティションキーを定義した場合、検索条件に値の指定が必須です。
aws dynamodb search-vectors \
--table-name Products \
--index-name ProductEmbeddingIndex \
--search-vector file://query-vector.json \
--top-k 10 \
--search-condition-expression "Category = :cat AND Brand = :brand" \
--expression-attribute-values '{":cat": {"S": "Electronics"}, ":brand": {"S": "Acme"}}' \
--projection-expression "ProductId, Title"
応答のSearchResultsにItemとScoreが並びます。API ReferenceのとおりTopKは1〜100で、ページングはありません。条件式で使える演算子は等号(=)だけです。INや範囲比較は未対応なので、価格帯や日付での絞り込みはこの段階では書けません。
バックフィル中のValidationExceptionを再試行で待つ作成直後の扱い
既存テーブルに追加したインデックスは、DescribeTableでIndexStatusがCREATINGかつBackfillingがtrueの間、検索するとValidationExceptionを返します。公式は、件数が少なくてもこの段階に長くかかることがあると書いています。
厄介なのはACTIVEになった直後です。検索は専用エンドポイントが受けるため、しばらく「The table does not have the specified index」が返ることがあります。公式の推奨どおり、実際の検索を再試行して最初の成功を準備完了とみなすのが確実です。
for i in $(seq 1 30); do
aws dynamodb search-vectors --table-name Products \
--index-name ProductEmbeddingIndex \
--search-vector file://query-vector.json --top-k 1 \
--search-condition-expression "Category = :cat" \
--expression-attribute-values '{":cat": {"S": "Electronics"}}' \
> /dev/null 2>&1 && { echo "ready"; break; }
sleep 20
done
デプロイ直後に検索を流すCI・CDのジョブには、このループを組み込んでおくと環境ごとの揺れで落ちません。
DynamoDBベクトル検索の料金3項目と100万件RAG試算の読み方
ベクトル検索の課金はベーステーブルの読み書きとは別建てです。テーブル側の単価と東京リージョンの実額はDynamoDBの料金を東京リージョンの実額で試算する記事にまとめているので、ここではベクトル部分の上乗せだけを扱います。
書き込み・検索・ストレージの3軸課金とバイト単位・最小1KBの規則
DynamoDBの料金ページによれば、ベクトル部分の課金は3軸です。インデックスへ書いたデータ量(GB)、検索で処理・返却したデータ量(GB)、インデックスの保存量(GB月)。書き込みと検索はバイト単位で、1回あたり最小1KBが計上されます。
検索の処理量はアイテム数に比例せず、対数的に増えると明記されている点は見積もりで効く。テーブルクラスをStandard-IAにすると、保存はStandardの40%、リクエストは125%の単価になります。グローバルテーブルでは、複製先リージョンでも書き込みが通常単価で計上されます。
公式試算の100万件RAGで月55.54USD、ベーステーブル分は別に65.75USD
料金ページにはバージニア北部リージョンでの試算例が載っています。100万件のRAG構成で、ベクトル書き込みが99GB分(1GBあたり0.52USD)で51.42USD、検索が1,586GB分(1GBあたり0.002USD)で3.17USD、保存3.82GB分(1GB月0.25USD)で0.95USD、合計55.54USDです。ベーステーブル側は別に65.75USDかかります。
読み取るべきは内訳の偏りです。費用の9割強は書き込みで、検索はごくわずかでした。埋め込みを頻繁に作り直す設計ほど請求が膨らむため、本文が変わっていない更新ではベクトル属性を書き換えないことが、単価を抑えるいちばん効く手になります。東京リージョンの単価はこの例に載っていないので、見積もり時に料金ページで確認してください。
本番で詰まる落とし穴:無音の索引漏れからテナント分離の誤解まで
CLIで動かすところまでは迷いません。問題が出るのは、運用データが入り、複数テナントが同じテーブルを使い始めてからです。公式ドキュメントが注意書きで済ませている挙動のうち、設計に影響する3点を取り上げます。
パーティションキー欠落で検索に出ない無音の索引漏れを防ぐ書き込み検査
インデックスにパーティションキーを定義した状態で、その属性を持たないアイテムを書くと、ベーステーブルへの書き込みは成功します。ただしベクトルインデックスには入らず、エラーも出ません。UpdateItemで属性を消した場合も同様に、検索結果から黙って消えます。
この挙動は単体テストでは気付けない。対策はアプリ側の書き込み前検査で、ベクトル属性を持つのにパーティションキー属性が無いアイテムを拒否する層を1か所に置きます。属性の型がスキーマと違う場合は書き込み自体が拒否されるので、そちらはエラーで気付けます。
パーティションキーは認可境界ではない:マルチテナントRAGでの分離方法
テナントIDをパーティションキーにすれば検索範囲はテナントごとに絞れます。しかし公式は、これはデータ局所性と性能のための仕組みで、アクセス制御ではないと明言しています。SearchVectors権限を持つ主体は、どのテナントの値でも検索できる。dynamodb:LeadingKeysによるきめ細かいアクセス制御もSearchVectorsには効きません。
受託でSaaSのRAGを組むなら、テナントIDはサーバー側で認証情報から決め、クライアントから受け取らない実装が最低線です。契約上データ層での分離まで求められる場合は、テーブルかインデックスをテナント別に分け、IAMの許可も別にします。インデックスは1テーブル5個までなので、テナント数が多ければテーブル分割になります。
原文更新後も古い埋め込みが残る問題とStreamsで再生成する設計
DynamoDBは埋め込みを再計算しません。商品説明を書き換えてもEmbeddingは古いままで、検索は旧本文の意味で当たり続けます。誤った結果が返っているのに、エラーは一切出ない種類の不具合です。
DynamoDB Streamsはベクトルインデックスと独立して動くので、Streamsで本文属性の変更を拾い、Lambdaで埋め込みを作り直して書き戻す構成が組めます。その際、本文が実際に変わったときだけ再生成する条件を入れてください。料金章のとおり、書き込みが費用の大半を占めるからです。
S3 Vectors・OpenSearchと比べたDynamoDBベクトル検索の採用判断
AWSでベクトルを置く先は、DynamoDBのほかにS3 VectorsとOpenSearchがあります。違いは「何件をどの頻度で、どんな条件で引くか」で決まります。
S3 Vectorsとの比較表:TopK上限・フィルタ・規模で見る住み分け
両者の上限値をDynamoDBのクォータとS3 Vectorsの制限一覧から並べました(2026年10月時点)。
| 項目 | DynamoDB | S3 Vectors |
|---|---|---|
| 最大次元数 | 4,096 | 4,096 |
| 1回の取得上限 | TopK 100 | TopK 10,000 |
| インデックスの規模 | 容量上限なし | 1索引20億ベクトル |
| フィルタの属性 | インライン18個 | メタデータ2KBまで |
| 正本データとの関係 | 同じアイテムに同居 | 別ストアに複製 |
DynamoDBの強みは最後の行です。注文・会員・在庫と同じアイテムにベクトルがあり、削除やTTLがそのまま検索から消える。S3 Vectorsは大量のベクトルを安く置き、多めに候補を取ってから絞る使い方に向きます。S3 Vectorsの操作と料金はAmazon S3 Vectors APIとは?操作一覧・料金・OpenSearch比較とRAG構築を解説で詳しく扱っています。
DynamoDBのベクトル検索を採用する条件と見送るべき場面の線引き
採用してよいのは、次の条件がそろう場合です。正本がすでにDynamoDBにある、検索をテナントやカテゴリの等号条件で区切れる、1回の候補は100件以内で足りる、の3点。会員ごとのレコメンドや、テナント別のFAQ検索がこの型に当たります。
見送るのは、日付・価格の範囲条件やキーワード検索との組み合わせが要件に入る場合です。等号フィルタしか無い現状では、ハイブリッド検索を求める社内文書RAGはOpenSearchのほうが素直に組めます。ベースのテーブルが600GBを超える場合も、インデックス作成にAWSサポートでの許可が必要です。どの基盤で組むべきか判断がつかないときは、RAG構築支援で既存データの置き場所から一緒に設計できます。
よくある質問
DynamoDBのベクトル検索を検討する段階でよく出る疑問に答えます。
DynamoDBのベクトル検索はどのリージョンで使えますか?
要件と制限のページによると、すべての商用リージョンに加え、AWS GovCloud(US)と中国リージョンで使えます。東京リージョンも対象です。ただし料金ページの試算例はバージニア北部の単価なので、東京の単価は別途確認が必要です。
プロビジョンドキャパシティのテーブルにベクトルインデックスを追加できますか?
できません。ベクトルインデックスはオンデマンド専用で、テーブル自体もオンデマンドである必要があります。先にUpdateTableで課金モードをPAY_PER_REQUESTへ切り替えてから追加します。予約キャパシティを購入済みのテーブルでは、その分の割引が使えなくなる点を費用比較に入れてください。
DAXを使っている構成でSearchVectorsもキャッシュできますか?
DAXはSearchVectorsに対応していません。DAXを経由している構成でも、ベクトル検索だけはDynamoDB本体へ直接送ります。検索結果をキャッシュしたい場合は、ElastiCache for Valkeyなど外部キャッシュに自前で載せ、無効化もアプリ側で管理する必要があります。
グローバルテーブルでもベクトル検索は使えますか?
使えます。インデックスの定義は複製先リージョンへ自動で作られ、各リージョンで同じデータを検索できます。ただしインデックスへの反映は書き込みの後に非同期で行われ、MRSCの強い整合性もテーブルまでで、インデックスには及びません。複製の仕組みはDynamoDB Global Tablesとはで解説しています。
書き込んだ直後のベクトルはすぐ検索結果に出ますか?
すぐには出ません。検索結果は結果整合性で、書き込みから検索に反映されるまで短い遅延があります。登録直後に「自分が今書いた文書」を必ず返したい画面では、検索結果に直前の書き込みをアプリ側で足すか、反映を待つ表示を入れる設計にします。
関連記事
- Amazon DynamoDBとは:機能の全体像からローカル起動・キー設計・容量モード選定まで:ベクトル検索の前提となるキー設計と容量モード
- Amazon S3 Vectors APIとは?操作一覧・料金・OpenSearch比較とRAG構築を解説:大量ベクトルを安く置く側の選択肢
- ベクトルDBとは?仕組み・インデックス構造・製品比較とpgvectorで動かす手順:ANNの索引構造と専用製品の比較
- ベクトル検索とセマンティック検索の違い:包含関係・仕組み・pgvectorでの実装と使い分け:検索方式の整理
- DynamoDBの料金を東京リージョンの実額で試算する:請求が跳ねる設計と単価の下げ方:ベーステーブル側の費用