Why this matters
- VaultCommerce needs orders by user (primary key) and orders by status for ops dashboards (GSI) — without scanning the table nightly.
- GSIs replicate data asynchronously — expect eventual consistency and plan for write amplification on hot items.
- LSIs must be defined at create time and share the 10 GB per-partition limit with the base table — rarely added later.
Global Secondary Index
A GSI has its own partition and sort key drawn from table attributes:
aws dynamodb update-table \
--table-name VaultCommerceOrders \
--attribute-definitions \
AttributeName=GSI1PK,AttributeType=S \
AttributeName=GSI1SK,AttributeType=S \
--global-secondary-index-updates '[{
"Create": {
"IndexName": "GSI1-StatusDate",
"KeySchema": [
{ "AttributeName": "GSI1PK", "KeyType": "HASH" },
{ "AttributeName": "GSI1SK", "KeyType": "RANGE" }
],
"Projection": { "ProjectionType": "INCLUDE", "NonKeyAttributes": ["totalAmount","customerName"] },
"ProvisionedThroughput": { "ReadCapacityUnits": 5, "WriteCapacityUnits": 5 }
}
}]'
VaultCommerce writes GSI1PK=STATUS#SHIPPED, GSI1SK=2026-04-01T10:00:00Z on each order update so ops queries pending shipments by status.
GSI vs LSI
- GSI — new partition key; sparse indexes OK; add after creation; eventually consistent reads only.
- LSI — same partition key, alternate sort key; must exist at table create; supports strong reads on same partition.
- Projection —
ALL,KEYS_ONLY, orINCLUDEspecific attributes — trade storage vs query efficiency.
Querying a GSI
from boto3.dynamodb.conditions import Key
table.query(
IndexName="GSI1-StatusDate",
KeyConditionExpression=Key("GSI1PK").eq("STATUS#SHIPPED")
& Key("GSI1SK").between("2026-04-01", "2026-04-02"),
)
Each GSI consumes its own WCU on every base-table write that touches projected attributes — budget for dual writes during peak checkout.
Local Secondary Index example
For a table keyed PK=USER#id, SK=ORDER#date, an LSI LSI1-SK=AMOUNT#... lets you sort the same user's orders by amount without a GSI — but only within that user's partition.
# Defined only at CreateTable time
"LocalSecondaryIndexes": [{
"IndexName": "LSI1-Amount",
"KeySchema": [
{ "AttributeName": "PK", "KeyType": "HASH" },
{ "AttributeName": "AmountSK", "KeyType": "RANGE" }
],
"Projection": { "ProjectionType": "ALL" }
}]
VaultCommerce prefers GSIs for ops cross-partition queries; LSIs when multiple sort orders per user partition are known at design time.
Quick recall
Everything you need if you only revisit this box.
- GSI: alternate partition+sort keys; add anytime; separate throughput; eventual reads.
- LSI: same partition key, different sort key; create-time only; strong reads possible.
- Project only attributes queries need —
INCLUDEsaves storage vsALL. - GSI writes add WCU overhead — every base update may touch multiple indexes.
- VaultCommerce GSI queries orders by shipment status for ops dashboards.
- Sparse index keys keep rarely queried states out of the index.
Test yourself
Answer these before moving on — recall is what makes it stick.