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 indexesDer 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
IndexNameersetztTableNamenicht. Beide gehen in denselben Aufruf, und dieKeyConditionExpressionbenennt 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_ONLYoder dieINCLUDE-Liste); laut API-Referenz gilt: "global secondary index queries cannot fetch attributes from the parent table". Ein fehlendes Attribut bedeutet ein nachgelagertesget_itemauf 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 undDecimalheraus. - 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
- Einen GSI in DynamoDB mit Node.js abfragen — dieselbe Index-Abfrage mit AWS SDK v3.
- Einen GSI in DynamoDB mit der AWS CLI abfragen — dieselbe Index-Abfrage aus der Shell.
- DynamoDB Query in Python — die Basistabelle abfragen.
- GSI vs. LSI — welcher Indextyp zum Zugriffsmuster passt.
- Warum GSIs letztendlich konsistent sind — die Replikationsverzögerung erklärt.
- "The table does not have the specified index" — der Indexname passt nicht (GSI-Namen sind case-sensitiv).
- "Consistent reads are not supported on global secondary indexes" — warum das Consistent-Read-Flag auf einem GSI scheitert.