Amazon Web Services ブログ

Amazon DynamoDB のネイティブベクトルサポートでセマンティック検索を構築する

Amazon DynamoDB を運用データに使用しているアプリケーションの多くは、ベクトル類似性検索も必要としています。これまでは、別途ベクトルデータベース、同期パイプライン、そしてそれらに伴うアーキテクチャの複雑さとコストの増加が必要でした。DynamoDB はネイティブベクトル検索をサポートするようになりました。ベクトル埋め込み (embedding) をアプリケーションデータと一緒に保存し、DynamoDB で直接類似性検索を実行できます。これにより、単一のテーブルが運用データストアとベクトルストアの両方の役割を果たします。

この記事では、DynamoDB のネイティブベクトル検索を紹介し、研究論文のアブストラクトに対するセマンティック検索アプリケーションを構築します。Amazon Bedrock で埋め込みを生成し、DynamoDB に保存し、セマンティックに検索します。また、コストを見積もり、コントロールできるように、ベクトル検索がどのように課金されるかについても説明します。

なぜ DynamoDB にベクトルを保存するのか

運用データを 1 つのデータベースに、ベクトル埋め込みを別のベクトルストアに維持することには、次のような課題があります。

  • データ同期 – ベクトルをソースデータと同期させ続けることは運用オーバーヘッドを増やし、古い結果を返すリスクがあります。
  • 高いレイテンシー – ベクトルストアで最近傍を見つけた後、実際のアイテムデータを取得するためにプライマリデータベースへのラウンドトリップが必要になります。
  • コスト増加 – 1 つで両方の目的を果たせるところを 2 つのデータベースを実行して料金を払う必要があります。
  • アーキテクチャの複雑さ – サービスが増えると障害モードも運用も増えます。

DynamoDB ベクトル検索では、ベクトルをアイテムの属性として保存でき、これらの課題に対処できます。ベクトルインデックスで類似性検索を実行すると、DynamoDB は結果と一緒にアイテムデータを返します。他の場所で ID を検索する必要はありません。

主なメリット

  • サーバーレスかつフルマネージド – プロビジョニング、パッチ適用、スケーリングするインフラストラクチャはありません。ベクトルインデックスと検索は自動的にスケールします。
  • ワークロードに応じてスケール – ベクトル検索は、DynamoDB が運用ワークロードをスケールするのと同様に水平方向にスケールするため、インデックス内で数兆のベクトルを保存および検索できます。
  • 予測可能な低レイテンシーのパフォーマンス – データセットとスループットが増加しても、類似性検索は一貫したレイテンシーで実行されます。
  • 既存データで動作 – 既存のテーブルにベクトルインデックスを追加し、DynamoDB にすでに保存されているアイテムにベクトル属性を追加します。あとは DynamoDB が処理します。
  • 組み込みのフィルタリング – インデックスの検索スキーマで定義された属性でフィルタリングと組み合わせてベクトル類似性を利用できます。
  • 使用量に応じた支払い – DynamoDB の課金モデルに合わせて、使用した分だけ支払います。

ユースケース

DynamoDB のネイティブベクトル検索は、すでに DynamoDB をプライマリデータストアとして使用しており、アーキテクチャのオーバーヘッドを追加せずにデータをセマンティックに検索したいアプリケーションに適しています。一般的なユースケースには次のようなものがあります。

  • セマンティック検索 – データに正確なキーワードが現れない場合でも、意味に基づいて自然言語クエリと一致するアイテムを見つけます。
  • レコメンデーションエンジン – 説明、画像、またはユーザーの行動のベクトル的な近さに基づいて類似アイテムを提案します。
  • 検索拡張生成 (RAG) – 大規模言語モデル (LLM) にとって最も関連性の高いコンテキストを取得し、応答の品質を向上させ、ハルシネーションを減らします。
  • エージェント記憶 – 会話の要約、学習した事実、過去の決定を埋め込みとして保存し、ベクトル検索で最も関連性の高いものを取得することで、AI エージェントに長期記憶を与えます。
  • 異常検知と不正検知 – ベクトルを比較して、通常のクラスターから大きく外れているトランザクションや行動、または既知の不正パターンに近いものをフラグ付けします。

ソリューション概要

このウォークスルーでは、Hugging Face ウェブサイトの arxiv-abstracts-2021 データセットと、埋め込みを生成する Bedrock を使用して、研究論文のアブストラクトに対するセマンティック検索アプリケーションを Python で構築します。結果は、「ブラックホール合体からの重力波の検出」 のようなクエリに応答できる DynamoDB テーブルとなります。論文本文に該当の単語が現れない場合でも、最も関連性の高い論文を返します。

以下の図はアーキテクチャを示しています。データを投入するために、アプリケーションは Bedrock で各アイテムの埋め込みを生成します。次に、アイテムをその埋め込みと共に、ベクトルインデックスを持つ DynamoDB テーブルに保存します。検索するには、アプリケーションは Bedrock を使用して自然言語クエリの埋め込みを生成し、その埋め込みでベクトルインデックスをクエリして、最も関連性の高い論文を返します。

アーキテクチャ図。アプリケーションが Amazon Bedrock で埋め込みを生成し、ベクトルインデックスを持つ DynamoDB テーブルにアイテムと埋め込みを書き込み、そのインデックスをクエリして一致する論文を返す様子を示しています

図 1: Amazon Bedrock からの埋め込みで DynamoDB ベクトルインデックスにデータを投入して検索する

大規模な一致を素早く見つけるために、ベクトルインデックスは近似最近傍 (ANN) 検索を使用します。データセットが大きくなるにつれて、格納されているすべてのベクトルに対してクエリを比較することは非常に高価で遅くなります。ANN は代わりに、少しの正確性を大幅な速度向上と引き換えにします。真の最近傍に近い結果を返しつつ、検索を高速に、コストを予測可能に保ちます。

ウォークスルーは 3 つのステップで構成されます。

  1. ベクトルインデックスを持つ DynamoDB テーブルを作成する。
  2. Bedrock で埋め込みを生成し、テーブルにデータを投入する。
  3. 自然言語クエリでベクトルインデックスを検索する。

このウォークスルーの完全に実行可能なコードは、GitHub リポジトリで入手できます。

前提条件

このウォークスルーには以下が必要です。

  • AWS アカウント。
  • Python 3.12 以降。
  • 環境用に設定された AWS 認証情報で、DynamoDB および Bedrock API を呼び出す権限を持つもの。
  • AWS リージョンでの Bedrock と Amazon Titan Text Embeddings V2 モデルへのアクセス。モデルへのアクセスをリクエストする手順については、Bedrock ドキュメントを参照してください。
  • DynamoDB ベクトル検索のサポートを追加する AWS SDK for Python (Boto3) バージョン 1.43.64 以降。

ステップ 1: ベクトルインデックスを持つ DynamoDB テーブルを作成する

ベクトルインデックスはテーブル作成の一部として定義できます。ベクトルインデックスは、埋め込みを保持する属性、次元数、および距離関数を指定します。DynamoDB の他のセカンダリインデックスタイプと同様に、インデックスに射影する属性も指定します。

UpdateTable API で既存のテーブルにベクトルインデックスを追加することもできます。すでに保存されているデータでベクトル検索を使用するためにテーブルを再作成する必要はありません。

以下のコードはテーブルとそのベクトルインデックスを作成します。

import boto3

dynamodb = boto3.client('dynamodb')

# ベクトルインデックスを持つテーブルを作成する
dynamodb.create_table(
    TableName='Papers',
    AttributeDefinitions=[
        {'AttributeName': 'paper_id', 'AttributeType': 'S'},
    ],
    KeySchema=[
        {'AttributeName': 'paper_id', 'KeyType': 'HASH'},
    ],
    VectorIndexes=[
        {
            'IndexName': 'VectorIndex',
            'VectorAttribute': {'AttributeName': 'embedding'},
            'Dimensions': 1024,
            'DistanceFunction': 'DOT_PRODUCT', # COSINE | DOT_PRODUCT | EUCLIDEAN
            'Projection': {
                'ProjectionType': 'ALL', # ALL | KEYS_ONLY | INCLUDE
            }
        }
    ],
    BillingMode='PAY_PER_REQUEST'
)

print("Table 'Papers' created with vector index. The table will be ready shortly.")

以下の設定の選択肢を検討してください。

  • Dimensions – インデックス内のベクトルの長さ。この値は埋め込みモデルの出力と一致する必要があります。DynamoDB は最大 4,096 次元のベクトルをサポートします。
  • Distance function – DynamoDB がベクトルを比較し、類似性で結果をランク付けするために使用する尺度。この例では、埋め込みが生成時に単位ベクトルに正規化されるため (ステップ 2 参照)、DOT_PRODUCT を使用します。単位ベクトルの場合、ドット積はコサイン類似度と等しくなるため、DOT_PRODUCTCOSINE と同じランキングを提供しつつ、コサインの内部正規化ステップをスキップします。ベクトルがまだ正規化されていない場合は COSINE を使用します (内部で正規化するため)。マグニチュードが重要な場合は EUCLIDEAN を使用します。COSINEEUCLIDEAN では、スコアが低いほど類似性が高いことを示します。DOT_PRODUCT では、スコアが高いほど類似性が高いことを示します。
  • Projection – この設定は、どの属性がインデックスにコピーされ、したがって検索結果として返せるかを決定します。ベクトルとベーステーブルのキー属性は含まれます。INCLUDE はリストされた属性を追加し、ALL はすべてを追加し、KEYS_ONLY は何も追加しません。この例では ALL を使用しており、検索が単一の呼び出しでタイトル、アブストラクト、著者を含む完全な論文レコードを返します。多数または大きな属性を持つワークロードでは、必要なもののみを射影する INCLUDE を検討してください。これにより、インデックスが小さくなり、検索が安くなります。
  • インラインフィルター (オプション) – ベクトル検索は、検索中に射影された属性の組み合わせでフィルタリングできます。この例ではインラインフィルタリングを使用しません。これは別のブログ記事で扱います。
  • パーティションキー (オプション) – ベクトルインデックスの全体的なスループットをスケールし、大規模なデータセットの検索あたりのコストを削減します。ベクトルインデックスは分散されているため、パーティションキーを定義するかどうかに関係なく水平方向にスケールします。インデックスのパーティションキーを定義すると、すべてのベクトル検索でその値を提供する必要があります。DynamoDB はその後、その値を持つアイテムを保持するインデックスの部分に検索を制限します。これにより、DynamoDB が応答するために処理するデータが少なくなるため、検索のコストが削減されます。スループットのクォータはパーティションキーの値ごとに適用され、パーティションキーの各異なる値には独自のクォータが与えられます。したがって、パーティションキーを定義することで、インデックスが維持できる書き込みおよび検索の全体的なスループットも上がります。この例ではパーティションキーを使用しないため、すべての検索がインデックス全体をカバーします。パーティションキーの使用は別のブログ記事で扱います。

テーブルとベクトルインデックスの作成は非同期で行われます。データをロードする前に、DescribeTable API を使用して、テーブルとインデックスが ACTIVE であり、バックフィルが完了していることを確認します。

import time

def wait_until_active(table_name, index_name):
    while True:
        table = dynamodb.describe_table(TableName=table_name)['Table']
        table_status = table['TableStatus']

        # 対象のベクトルインデックスを検索
        index_status = None
        backfilling = False
        for vi in table.get('VectorIndexes', []):
            if vi['IndexName'] == index_name:
                index_status = vi['IndexStatus']
                backfilling = vi.get('Backfilling', False)
                break

        print(f"Table: {table_status} | Vector index: {index_status} | Backfilling: {backfilling}")

        if table_status == 'ACTIVE' and index_status == 'ACTIVE' and not backfilling:
            print("Table and vector index are ready.")
            return table

        time.sleep(5)

table = wait_until_active('Papers', 'VectorIndex')

テーブルの準備ができると、テーブルとインデックスは ACTIVE を報告し、Backfilling: False となります。

Table: CREATING | Vector index: CREATING | Backfilling: False
Table: ACTIVE | Vector index: ACTIVE | Backfilling: False
Table and vector index are ready.

ステップ 2: 埋め込みを生成してテーブルにデータを投入する

次のステップでは、データセットから最初の 1,000 件の論文を読み込み、Bedrock (Amazon Titan Text Embeddings V2) でベクトル埋め込みを生成し、すべてを DynamoDB に保存します。

import boto3
import gzip
import json
import requests

DATASET_URL = 'https://huggingface.co/datasets/gfissore/arxiv-abstracts-2021/resolve/main/arxiv-abstracts.jsonl.gz'
SAMPLE_SIZE = 1000 # 最初の 1K 論文のみを含める

def clean(text):
    """空白と改行を単一のスペースに折りたたむ"""
    return ' '.join((text or '').split())

def load_papers():
    """データセットをストリームし、最初の SAMPLE_SIZE 論文のみを読み込む

    ファイルは gzip 圧縮された JSON Lines コーパス (約 940 MB) です。ストリーミングは、
    すべてをダウンロードするのではなく、SAMPLE_SIZE レコードに必要なバイト数のみを読み込みます。
    """
    papers = []
    headers = {'User-Agent': 'aws-dynamodb-vector-search-sample'}
    with requests.get(DATASET_URL, stream=True, headers=headers) as resp:
        resp.raise_for_status()
        # Hugging Face は不透明な .gz ボディを提供するため、明示的に解凍します。
        resp.raw.decode_content = False
        with gzip.GzipFile(fileobj=resp.raw) as gz:
            for _, line in zip(range(SAMPLE_SIZE), gz):
                papers.append(json.loads(line))
    return papers

# クライアントを初期化
bedrock = boto3.client('bedrock-runtime')
dynamodb = boto3.client('dynamodb')

def generate_embedding(text):
    """Amazon Bedrock Titan Embeddings V2 を使用してベクトル埋め込みを生成する"""
    response = bedrock.invoke_model(
        modelId='amazon.titan-embed-text-v2:0',
        contentType='application/json',
        accept='application/json',
        body=json.dumps({
            # Titan の入力上限 8,192 トークン以下に収めるために切り詰めます。
            # 20,000 文字は安全なマージンを残します。
            'inputText': text[:20000],
            'dimensions': 1024, # ベクトルインデックスの設定と一致させる
            'normalize': True
        })
    )
    result = json.loads(response['body'].read())
    return result['embedding']

papers = load_papers()
print(f"Loaded {len(papers)} papers. Generating embeddings and writing to DynamoDB...")

# 埋め込み付きで論文を DynamoDB に書き込む
for i, paper in enumerate(papers):
    title = clean(paper.get('title'))
    abstract = clean(paper.get('abstract'))

    # 豊かな埋め込みのためにタイトルとアブストラクトを結合
    text_to_embed = f"{title}. {abstract}"
    embedding = generate_embedding(text_to_embed)

    # 埋め込みを数値のリストとして保存
    embedding_list = {'L': [{'N': str(v)} for v in embedding]}

    dynamodb.put_item(
        TableName='Papers',
        Item={
            'paper_id': {'S': paper['id']},
            'title': {'S': title},
            'abstract': {'S': abstract},
            'authors': {'S': clean(paper.get('authors'))},
            'embedding': embedding_list, # ベクトル属性
        }
    )

    if (i + 1) % 100 == 0:
        print(f"  Processed {i + 1}/{len(papers)} papers...")

print(f"Done! {len(papers)} papers stored with embeddings.")

この例では、論文のタイトルとアブストラクトを一緒に埋め込むことで、論文の内容をキャプチャするテキスト表現を作成します。「画像を認識するためにコンピュータを訓練する」という検索クエリは、画像分類や物体検出に関する論文にマッチします。意味が近ければ、タイトルにその正確な単語が現れなくてもマッチします。タイトルとアブストラクトを埋め込むのは、それらが一緒に論文の核心的な意味を持つからです。著者はアイテムに引き続き保存されるため、各結果と一緒に返されます。

このスクリプトは論文を一度に 1 つずつ処理するため、1,000 件すべての論文をロードするのに約 2〜4 分かかります (1 論文あたり数百ミリ秒)。その時間のほとんどは Bedrock の埋め込み呼び出しに費やされます。

ベクトルインデックスにデータを投入する各書き込みは、ベクトル書き込み容量も消費します。PutItem の呼び出しに ReturnConsumedCapacity='INDEXES' を設定して、ベクトル書き込みのコストを確認します。応答の ConsumedCapacity オブジェクトは、テーブル自身の書き込み容量と並んで、VectorIndexes マップの下に各ベクトルインデックスで消費された VectorWriteRequestBytes を報告します。TOTAL はテーブルの書き込み容量のみを返しベクトルインデックスのコストは省略されるため、TOTAL ではなく INDEXES を使用してください。

ReturnConsumedCapacity='INDEXES' を設定すると、返される ConsumedCapacity は次のようになります。

{
    "TableName": "Papers",
    "CapacityUnits": 5.0,
    "Table": { "CapacityUnits": 5.0 },
    "VectorIndexes": {
        "VectorIndex": { "VectorWriteRequestBytes": 4820.0 }
    }
}

テーブル自身の書き込み容量とベクトルインデックスの書き込みコストは別々に報告されるため、書き込みのコストのどれだけがベクトルインデックスの維持から発生しているかを確認できます。このコストの課金方法と価格設定については、Amazon DynamoDB の料金ページをご覧ください。

ステップ 3: ベクトルインデックスを検索する

データがロードされると、テーブルはセマンティック検索の準備が整います。以下のコードでは、ステップ 2 の generate_embedding 関数を使用して自然言語クエリを埋め込み、SearchVectors API を呼び出して最も類似する論文を見つけます。

import boto3
import json

bedrock = boto3.client('bedrock-runtime')
dynamodb = boto3.client('dynamodb')

def search_papers(query_text, top_k=5):
    """セマンティック類似性を使用して論文を検索する"""
    # 検索クエリの埋め込みを生成
    query_embedding = generate_embedding(query_text)

    # 検索ベクトルは数値のリストで、保存された属性と同じ形状
    search_vector = [{'N': str(v)} for v in query_embedding]

    # ベクトル検索を実行
    response = dynamodb.search_vectors(
        TableName='Papers',
        IndexName='VectorIndex',
        SearchVector=search_vector,
        TopK=top_k,
        ReturnConsumedCapacity='INDEXES',
    )
    return response

# サンプル検索
queries = [
    "detecting gravitational waves from black hole mergers",
    "improving the efficiency of solar cells",
    "quantum error correction for fault-tolerant computing",
]

for query in queries:
    print(f"\nQuery: \"{query}\"")
    print("-" * 50)
    response = search_papers(query, top_k=3)

    for rank, result in enumerate(response['SearchResults'], 1):
        item = result['Item']
        score = result.get('Score', 'N/A')
        title = item['title']['S']
        print(f"  {rank}. {title} (score: {score})")

    # 応答は、クエリが処理したベクトル検索のバイト数を報告する
    consumed = response.get('ConsumedCapacity', {})
    print(f"  VectorSearchRequestBytes: {consumed.get('VectorSearchRequestBytes')}")

SearchVectors の応答には SearchResults リストが含まれます。各要素は 2 つの部分を持ちます。

  • Item – ベクトルインデックスから一致したアイテムの射影された属性。
  • Score – インデックスの距離関数に基づいて、結果がクエリベクトルにどれだけ近いかを示す類似性スコア。

ReturnConsumedCapacityINDEXES または TOTAL に設定すると、応答には、クエリが処理した VectorSearchRequestBytes を報告する ConsumedCapacity オブジェクトも含まれます。デフォルトの NONE は、これを省略します。ベクトル検索はベクトルインデックスからのみ読み取るため、内訳を出す別のベーステーブル容量はありません。ここでは、INDEXESTOTAL は同じ数値を返します。これは、書き込みとは異なります。書き込みでは、INDEXES のみがベーステーブルの書き込みに加えてベクトルインデックスのコストを露出します。

デフォルトでは、ベクトル属性はアイテムと一緒に返されません。その理由と、リクエストする方法については「知っておくべきこと」パートをご覧ください。

ベクトルインデックスの使用量はどのように課金されるか

ベクトルインデックスは、アイテムを保持する基礎となるテーブルに対する標準の DynamoDB 課金に加えて、3 つの次元で課金されます。インデックスに書き込むデータ、検索時に処理されるデータ、および保存するデータに対して料金が発生します。3 つすべてがバイトごとに計測され、GB ごとに課金されるため、プロビジョニングする容量ユニットはなく、各オペレーションが実行する作業に比例してコストがスケールします。

  • ベクトル書き込みリクエスト – ベクトルインデックスに書き込まれたデータに対して料金を支払います。これには、ベクトル自体とインデックスに射影された非ベクトル属性が含まれます。ベクトル属性や射影された属性に触れない書き込みは、ベクトル書き込みとして課金されません。これらの料金は、ベーステーブルの標準書き込み料金に加えて発生します。
  • ベクトル検索 – 類似性検索に応答するために処理されたデータに対して料金を支払います。インデックスがパーティションキーを定義している場合、検索は検索されたパーティションキーの値を持つアイテムを保持するインデックスの部分に制限されます。これにより、処理されるデータが減少し、したがって検索コストが削減されます。
  • ストレージ – ベクトルインデックスに保存されているデータに対して、GB-月あたりの料金を DynamoDB テーブルストレージと同じレートで支払います。

各オペレーションは、応答の ConsumedCapacity オブジェクトで消費されたベクトル容量を報告するため、コストを特定の書き込みや検索に帰属させることができます (ステップ 2 とステップ 3 を参照)。ベクトル使用量はバイトごとに計測されるため、これらの値は各オペレーションが処理したバイト数を追跡します。各ベクトル書き込みと各ベクトル検索には、最小課金サイズが 1 KB あります。1 KB を下回るオペレーションは 1 KB として計測され、それを超える場合はバイトごとに計測されます。これは、書き込みでインデックスに書き込まれたデータと、類似性検索に応答するために処理されたデータの両方に適用されます。

Standard または Standard-Infrequent Access テーブルクラスを使用するテーブルでベクトルインデックスを使用できます。GB あたりの料金については、Amazon DynamoDB の料金を参照してください。

コストを最適化するには、以下のプラクティスを検討してください。

  • ユースケースが最大の精度を必要としない場合は、256 または 512 などのより低い次元を使用します。これにより、ストレージ、書き込みごと、検索ごとのコストが同時に削減されます。
  • 射影されたアイテムのサイズがストレージと検索が処理するバイト数の両方を左右するため、必要な属性のみをインデックスに射影します。
  • デフォルトでは埋め込みが結果に含まれないので、そのバイト数が検索料金にカウントされないようにしてください。必要なときにのみベクトルを射影します。
  • アクセスパターンに合う場合は、パーティションキーを持つインデックスを使用します。検索は検索された値を持つアイテムを保持するインデックスの部分のみをカバーし、より少ないバイトを処理します。

知っておくべきこと

アプリケーションを設計する際に、DynamoDB ベクトル検索の以下の特性を念頭に置いてください。

  • オンデマンド容量モード – ベクトルインデックスは、オンデマンド (PAY_PER_REQUEST) 容量モードを使用するテーブルでのみサポートされます。
  • アイテムは必要な属性を持つ場合にのみインデックス化される – アイテムは、ベクトル属性を含む場合にのみベクトルインデックスに追加されます。インデックスがパーティションキーを定義している場合、アイテムはインデックスに含まれるためにそのパーティションキー属性も含む必要があります。
  • 結果整合性 – ベクトルインデックスはアイテムがテーブルに書き込まれると非同期に更新されるため、ベクトル検索は結果整合性です。新しく書き込まれたか更新されたアイテムは、すぐに検索結果に表示されないかもしれませんが、インデックスが追いついた直後に検索可能になります。
  • ベクトルはデフォルトでは返されない – 埋め込みはインデックスに保存されていますが、SearchVectors は結果から除外します。これにより、処理されるバイト数、したがって検索コストが増大しないようにします。必要な場合は、検索の ProjectionExpression で明示的にリクエストします。
  • ベクトル次元 – DynamoDB は最大 4,096 次元のベクトルをサポートし、インデックスの次元は埋め込みモデルの出力と一致する必要があります。
  • 追加料金なしのバックフィルUpdateTable API で既存のテーブルにベクトルインデックスを追加すると、DynamoDB は無料でインデックスをバックフィルします。バックフィルは、テーブルのアイテムにすでに保存されているベクトル属性を使用します。
  • インデックス設定は不変 – ベクトルインデックスの設定は、インデックスが作成されたときに設定され、その後変更できません。これには、次元、距離関数、射影、インラインフィルター属性、およびパーティションキーが含まれます。これらのいずれかを変更するには、必要な設定で新しいベクトルインデックスを作成します。前述のように、DynamoDB は新しいインデックスを無料でバックフィルします。1 つのテーブルは、デフォルトで最大 5 つのベクトルインデックスを持つことができます。
  • セカンダリインデックスのベクトル属性 – アイテムにベクトル属性を追加すると、すべての属性を射影するセカンダリインデックスに影響します。ProjectionType: ALL で作成されたグローバルセカンダリインデックス (GSI) またはローカルセカンダリインデックス (LSI) は、新しいベクトル属性を自動的にインデックスにコピーします。これにより、インデックスのストレージ、書き込み、および読み取りのコストが増加する可能性があります。インデックスがベクトルを必要としない場合、そのインデックスが使用する属性のみを射影します。既存のすべての属性の GSI を、ベクトルを除外する特定の属性セットを射影するものに置き換えることができます。ただし、LSI はテーブルが作成されたときに固定され、その後追加または削除できないため、テーブル作成時に事前にその射影を計画してください。
  • パーティションキーごとのスループットクォータ – ベクトルインデックスの書き込みおよび検索のスループットは、パーティションキー値ごとに適用されるクォータの対象となります。現在の制限については、DynamoDB のクォータを参照してください。パーティションキーのないインデックスは、単一のクォータの対象となります。パーティションキーを持つインデックスは、パーティションキーの値ごとに個別のクォータを取得し、これによりインデックスが維持できる合計スループットが向上します。

クリーンアップ

将来の料金が発生しないように、このウォークスルーで作成したリソースを削除してください。DynamoDB テーブルを削除すると、そのベクトルインデックスも削除されます。

import boto3

dynamodb = boto3.client('dynamodb')
dynamodb.delete_table(TableName='Papers')

print("Table 'Papers' deletion initiated. The table will be removed shortly.")

Bedrock のオンデマンド埋め込み呼び出しはリクエストごとに課金されるため、このウォークスルーで削除する常設の Bedrock リソースはありません。

まとめ

DynamoDB のネイティブベクトル検索を使用すると、すでに運用データに使用しているデータベースで類似性検索を実行できます。別のベクトルストアなしで、よりシンプルなアーキテクチャ、より少ない運用オーバーヘッド、より低いレイテンシー、より低いコストを得ながら、DynamoDB のサーバーレススケーリングと予測可能なパフォーマンスを維持できます。

この記事では、ベクトルインデックスを持つ DynamoDB テーブルを作成し、Bedrock で埋め込みを生成し、DynamoDB にロードし、自然言語クエリでセマンティック検索を実行しました。セマンティック検索、レコメンデーションエンジン、または RAG アプリケーションを構築する場合でも、運用データとベクトルワークロードの両方に単一のサーバーレスサービスを使用できます。

始めるには、DynamoDB ベクトル検索のドキュメントGitHub の完全なコードサンプルを確認し、ベクトルインデックスを持つテーブルを作成してください。

論文のメタデータは arxiv-abstracts-2021 データセットからのもので、CC0 1.0 Universal (パブリックドメイン) の下でライセンスされています。


本記事は 2026 年 08 月 05 日 に公開された “Build semantic search with native vector support in Amazon DynamoDB” を翻訳したものです。

原文: https://aws.amazon.com/blogs/database/build-semantic-search-with-native-vector-support-in-amazon-dynamodb/

著者について

Leonid Koren

Leonid Koren

Leonid は AWS のプリンシパル NoSQL ソリューションアーキテクトで、既存のアプリケーションの近代化や NoSQL データベースを使用した新規アプリケーションのアーキテクチャ設計をお客様に支援しています。AWS に入社する前、Leonid は 2000 年代初頭からバックエンドシステムの設計と開発を行っていました。

Mo Kamioner

Mo Kamioner

Mo はイスラエルのエルサレムを拠点とする AWS のシニア DynamoDB ソリューションアーキテクトです。あらゆる規模のワークロードで 10 年以上 DynamoDB に携わり、世界中のお客様が DynamoDB の実装を設計、最適化、スケールすることを支援しています。Mo は複雑な問題に対するシンプルな解決策を見つけることに情熱を持っており、オフの時間には子供たちと IoT や Lego をいじっている姿が見られます。