Einen GSI in DynamoDB mit Python (boto3) abfragen

Eine GSI-Abfrage ist eine normale query plus IndexName, und AlbumTitle-index liefert uns Songs nach Album — ein Zugriffsmuster, das der Tabellen-Key aus Artist + SongTitle nicht bedienen kann. Was sich in Python ändert, ist die Fehlerbehandlung: Die zwei häufigsten Index-Fehler scheitern in unterschiedlichen Schichten von boto3, und nur einer davon lässt sich über die Exception-Klasse abfangen.

Code

import boto3

client = boto3.client("dynamodb")

paginator = client.get_paginator("query")

items = []
for page in paginator.paginate(
    TableName="Music",
    IndexName="AlbumTitle-index",
    KeyConditionExpression="#hashKey = :hashKeyValue",
    ExpressionAttributeNames={"#hashKey": "AlbumTitle"},
    ExpressionAttributeValues={":hashKeyValue": {"S": "Danzon"}},
):
    items.extend(page["Items"])

print(f"Found {len(items)} songs on the album")

except ValidationException kompiliert nicht einmal, geschweige denn fängt es

Füge der obigen Abfrage ConsistentRead=True hinzu, und boto3 wirft dies — auf der Client- wie auf der Resource-API:

botocore.exceptions.ClientError: An error occurred (ValidationException) when
calling the Query operation: Consistent reads are not supported on global
secondary indexes

Der naheliegende Handler ist except client.exceptions.ValidationException. Den gibt es nicht:

AttributeError: <botocore.errorfactory.DynamoDBExceptions object> has no
attribute ValidationException. Valid exceptions are: BackupInUseException,
... IndexNotFoundException, ... ProvisionedThroughputExceededException, ...

botocore erzeugt Exception-Klassen aus dem Service-Modell, und DynamoDB modelliert 33 davon. ValidationException ist ein Fehler auf Protokollebene und keine davon — der einzige verlässliche Zweig geht also über den Code:

except ClientError as exc:
    if exc.response["Error"]["Code"] == "ValidationException":
        ...

Die Asymmetrie ist real. Vertippst du dich beim Indexnamen, bekommst du IndexNotFoundException, die modelliert ist und sich über die Klasse abfangen lässt. Missbrauchst du das Konsistenz-Flag, bekommst du einen String-Vergleich. Beides sind Index-Fehler; nur einer hat einen Typ.

Der Cursor trägt auch den Tabellen-Key

Der Paginator versteckt LastEvaluatedKey, aber es lohnt sich zu wissen, was er auf einem Index enthält. Über 300 Songs auf einem Album:

page 1: Count 271  capacity 128.5  LastEvaluatedKey ['AlbumTitle', 'Artist', 'SongTitle']
page 2: Count  29  capacity  14.0  LastEvaluatedKey []

Ein GSI-Key ist nicht eindeutig, der Index-Key allein kann den Read also nicht fortsetzen; DynamoDB gibt Index-Key und Basistabellen-Key zusammen zurück. Eine handgestrickte Paginierung, die nur den Index-Key speichert, wiederholt oder verliert Items.

Am 2026-07-28 gegen DynamoDB Local (amazon/dynamodb-local) mit boto3 1.43.58 auf CPython 3.14.6 reproduziert. Der Fehlertext und die Key-Listen sind die Ausgabe der Bibliothek selbst.

Erklärung

  • IndexName ersetzt TableName nicht. Beide gehen in denselben Aufruf, und die KeyConditionExpression benennt den Partition Key des Index (AlbumTitle) mit demselben Operatorenkreis wie eine Tabellenabfrage.
  • Du bekommst die Projection und sonst nichts. Der Index liefert, was er projiziert (ALL, KEYS_ONLY oder die INCLUDE-Liste); laut API-Referenz gilt: "global secondary index queries cannot fetch attributes from the parent table". Ein fehlendes Attribut bedeutet ein nachgelagertes get_item auf den Basis-Key oder eine breitere Projection auf einem neuen Index.
  • Items, denen der Index-Key fehlt, tauchen nie auf — das Sparse-Index-Muster. Es hält einen Index über status = "OPEN" klein, und es ist auch der Grund, warum eine GSI-Abfrage weniger liefern kann als erwartet, ohne etwas zu werfen.
  • Die Resource-API nimmt dasselbe IndexName: table.query(IndexName="AlbumTitle-index", KeyConditionExpression=Key("AlbumTitle").eq("Danzon")), mit nativen Python-Werten hinein und Decimal heraus.
  • Ein GSI-Write landet nach dem Tabellen-Write. Die Replikation ist asynchron, ein Read-after-Write-Pfad gegen den Index geht also gelegentlich ins Leere. Ihn in einer engen Schleife zu wiederholen verbrennt Kapazität, ohne die Replikation zu beschleunigen.

Mach es visuell

Der DynamoDB Expression Builder schreibt die Index-Key-Condition und die typisierte Value-Map als boto3-fertiges Python — inklusive der {"S": ...}-Hüllen, auf denen der Client besteht und die die Resource-API verbietet.

Um dieselbe Index-Abfrage per Formular auf deine eigenen Tabellen zu richten und die Ergebnisse in einem paginierten Grid zu lesen, lade DynoTable herunter.

Verwandte Beispiele

Referenzen

Diesen Request visuell bauen

Stelle diese Operation im kostenlosen DynamoDB Query Builder zusammen — Key-Bedingung, Filter, Index, Limit, Sortierreihenfolge und eine Paginierungsschleife — und kopiere sie als lauffähiges SDK-v3-, CLI- oder boto3-Programm zurück.

DynamoDB Query Builder öffnen

Mit DynamoDB ohne die Console arbeiten

Ein schneller DynamoDB-Desktop-Client, der das echte SQL ausführt, das DynamoDB nicht kann — JOINs, GROUP BY, Aggregationen — mit visueller Bearbeitung und einem KI-Agenten auf deinen eigenen Bedrock-Schlüsseln.

30 Tage kostenlos testen, keine Kreditkarte — danach der Kostenlos-Tarif ohne Zeitlimit.