DVA-C02Chapter 50 of 101Objective 1.3

DynamoDB Global and Local Secondary Indexes

This chapter covers DynamoDB Global Secondary Indexes (GSIs) and Local Secondary Indexes (LSIs), two powerful features that enable flexible querying beyond the primary key. Understanding when and how to use each index type is critical for the DVA-C02 exam, as approximately 10-15% of DynamoDB questions test index design, query patterns, and trade-offs. You will learn the internal mechanics, configuration options, consistency models, and common exam traps — including the dreaded LSI partition size limit and GSI write capacity throttling.

25 min read
Intermediate
Updated May 31, 2026

The Library Card Catalog with Multiple Indexes

Imagine a massive library with millions of books, each with a unique shelf location (the primary key). The library has a main card catalog organized by ISBN (the partition key). To find a book by ISBN, you go to the catalog, look up the shelf location, and retrieve the book directly — this is a primary key lookup. Now, suppose you often search for books by author or by publication year. Without indexes, you would have to walk every shelf (a full table scan) to find all books by a given author. To speed this up, the library creates two additional card catalogs: one organized by author (a local secondary index) and one organized by publication year (a global secondary index). The author catalog is stored in the same room as the main catalog (same partition key layout), so you can only look up authors within a specific ISBN range. The year catalog is stored in a separate room (different partition key), so you can look up any year across the entire library. Each index card contains the shelf location, so you can go directly to the book without scanning the main catalog. However, these indexes take up extra space and must be updated whenever a book is added, removed, or moved — this adds write overhead. In DynamoDB, local secondary indexes (LSIs) share the partition key with the table and are limited to 10 GB per partition key value, while global secondary indexes (GSIs) have their own partition key and are more flexible but eventually consistent by default.

How It Actually Works

What Are DynamoDB Indexes and Why Do They Exist?

DynamoDB is a key-value and document database that provides fast, predictable performance at any scale. Its primary access pattern is based on the table's primary key, which consists of a partition key (hash key) and optionally a sort key (range key). Without indexes, you can only query items efficiently using the primary key. Secondary indexes allow you to query on alternative attributes, enabling flexible access patterns without requiring a full table scan.

A secondary index is a separate data structure that contains a subset of attributes from the base table, organized by an alternative key. DynamoDB maintains this index automatically as items are written, updated, or deleted in the base table. There are two types: - Global Secondary Index (GSI): An index with a partition key and optional sort key that can be different from the base table's primary key. The index spans all partitions of the base table. - Local Secondary Index (LSI): An index that has the same partition key as the base table but a different sort key. It is local to each partition key value.

How Indexes Work Internally

When you create a GSI, DynamoDB creates a new table-like structure that is partitioned by the GSI's partition key. Each item in the base table is projected into the GSI with the specified attributes. The GSI has its own read and write capacity units, separate from the base table. Write operations to the base table that affect the GSI consume write capacity from both the base table and the GSI. If the GSI's write capacity is insufficient, writes to the base table may be throttled — even if the base table has enough capacity.

For an LSI, the index is stored in the same partition as the base table items that share the same partition key value. This means LSI queries are strongly consistent (if you request strongly consistent reads) and have lower latency than GSI queries, which are eventually consistent by default. However, LSIs have a hard limit: the total size of all items (base table + LSI) for a given partition key value cannot exceed 10 GB. This is a critical exam point.

Key Components, Values, Defaults, and Limits

- GSI: - Partition key: Any scalar attribute (String, Binary, Number). - Sort key: Optional, any scalar attribute. - Can be created only on tables with a primary key (partition key or composite key). - Maximum of 20 GSIs per table (default limit, can be increased via service quota). - Each GSI has its own provisioned read/write capacity (or auto scaling). - GSI queries are eventually consistent by default; strongly consistent reads are not supported. - GSI keys can be projected: KEYS_ONLY, INCLUDE, or ALL. - Write capacity consumed: For each item write to the base table that affects the GSI, one write capacity unit (WCU) is consumed for items up to 1 KB. If the GSI's partition key is different, the write is charged to the GSI's WCU. - Throttling: If a GSI's write capacity is exhausted, writes to the base table are throttled (ProvisionedThroughputExceededException).

- LSI: - Same partition key as base table; different sort key. - Can be created only at table creation time. You cannot add an LSI to an existing table. - Maximum of 5 LSIs per table. - LSI queries support strongly consistent reads (if the base table supports it). - 10 GB limit per partition key value (sum of base table items and LSI items for that partition). - LSI uses the same write capacity as the base table (no separate capacity). - Projection: KEYS_ONLY, INCLUDE, or ALL.

Configuration and Verification

Creating a GSI using AWS CLI:

aws dynamodb update-table --table-name MyTable \
    --attribute-definitions AttributeName=Author,AttributeType=S \
    --global-secondary-index-updates \
        '[{"Create":{"IndexName":"AuthorIndex","KeySchema":[{"AttributeName":"Author","KeyType":"HASH"}],"Projection":{"ProjectionType":"ALL"},"ProvisionedThroughput":{"ReadCapacityUnits":5,"WriteCapacityUnits":5}}}]'

Creating an LSI (only during table creation):

aws dynamodb create-table --table-name MyTable \
    --attribute-definitions AttributeName=PK,AttributeType=S AttributeName=SK,AttributeType=S AttributeName=Date,AttributeType=S \
    --key-schema AttributeName=PK,KeyType=HASH AttributeName=SK,KeyType=RANGE \
    --local-secondary-indexes \
        '[{"IndexName":"DateIndex","KeySchema":[{"AttributeName":"PK","KeyType":"HASH"},{"AttributeName":"Date","KeyType":"RANGE"}],"Projection":{"ProjectionType":"ALL"}}]' \
    --billing-mode PAY_PER_REQUEST

Verifying index status:

aws dynamodb describe-table --table-name MyTable --query "Table.GlobalSecondaryIndexes[].IndexStatus"

Indexes can be in ACTIVE, CREATING, UPDATING, or DELETING state.

How Indexes Interact with Related Technologies

DynamoDB Streams: When an item is modified, the stream captures the changes. If a GSI or LSI is projected with ALL attributes, the stream record includes all attributes. If KEYS_ONLY, only keys are included. Streams are useful for replicating index data to other systems.

DAX (DynamoDB Accelerator): DAX caches query results, including GSI queries. However, DAX does not support strongly consistent reads for GSIs because GSIs are inherently eventually consistent.

Transactions: DynamoDB transactions support up to 25 items or 4 MB of data per transaction. Indexes are updated atomically as part of the transaction. If a transaction updates an item that is projected into a GSI, the GSI is updated within the same transaction.

Auto Scaling: You can configure auto scaling for GSI read/write capacity separately from the base table. This is important to prevent throttling on write-heavy workloads.

On-Demand Capacity: If you use pay-per-request billing, both the base table and GSIs share the same on-demand capacity pool. There is no separate capacity for GSIs, but write throttling can still occur if the table's throughput exceeds the per-partition limits (3000 RCU or 1000 WCU per partition).

Internal Mechanics of GSI Write Throttling

When a write to the base table triggers a GSI write, DynamoDB first writes to the base table (consuming base table WCU). Then it asynchronously writes to the GSI. If the GSI's write capacity is insufficient, the write is queued. If the queue grows too large (up to 15 minutes), DynamoDB begins throttling the base table writes. This is a common exam scenario: a GSI with insufficient write capacity causes throttling on the base table.

Querying with Indexes

To query a GSI, you must specify the index name and the GSI partition key equality condition. Optionally, you can use sort key conditions. Example:

import boto3
client = boto3.client('dynamodb')
response = client.query(
    TableName='MyTable',
    IndexName='AuthorIndex',
    KeyConditionExpression='Author = :author',
    ExpressionAttributeValues={':author': {'S': 'John Doe'}}
)

For LSI queries, you specify the index name and the same partition key as the base table. You can request strongly consistent reads by setting ConsistentRead=True.

Projection Types

KEYS_ONLY: Only the index keys (partition and sort) and the base table primary key are projected. Minimum storage and write cost.

INCLUDE: You specify a list of additional attributes to project beyond keys.

ALL: All attributes from the base table are projected. Maximum storage and write cost, but queries don't need to fetch from the base table.

Choosing the right projection is a trade-off between query performance and storage/write cost. If a query can be satisfied entirely from the index (i.e., all required attributes are projected), it uses only the index's read capacity. Otherwise, DynamoDB fetches the remaining attributes from the base table, consuming additional read capacity.

Exam-Relevant Details

LSI can only be created at table creation. This is a key exam fact. You cannot add an LSI later. GSIs can be added or modified at any time.

10 GB limit per partition key value for LSI. If any partition key value exceeds 10 GB total (base table items + LSI items), DynamoDB returns an ItemCollectionSizeLimitExceededException on writes.

GSI are eventually consistent. Strongly consistent reads are not supported on GSIs. If you need strong consistency, use an LSI or the base table.

GSI write throttling can throttle base table writes. This is a common exam trap. Always ensure GSIs have sufficient write capacity.

Indexes count toward the 10 GB limit per partition for LSIs, but not for GSIs. GSIs have their own storage.

You can query a GSI with any partition key value, whereas an LSI query must specify the same partition key as the base table.

Indexes can be sparse. If an item does not have the GSI partition key attribute, it is not included in the GSI. This can be used to filter items.

Summary of Differences

| Feature | LSI | GSI | |---------|-----|-----| | Partition key | Same as base table | Different from base table | | Sort key | Different from base table | Different from base table | | Creation time | Only at table creation | Any time | | Maximum per table | 5 | 20 | | Strongly consistent reads | Supported | Not supported | | Write capacity | Shared with base table | Separate | | Partition size limit | 10 GB per partition key | No per-partition limit | | Sparse index | No (must have same PK) | Yes (if item lacks GSI PK) | | Query flexibility | Limited to base table PK | Any partition key value |

Walk-Through

1

Design the Access Pattern

Identify the queries your application needs to support. For example, you have a Users table with primary key (UserID). You need to query by Email. This access pattern dictates the need for a GSI on Email. Write down the exact query: 'Find user by email'. Determine if you need all attributes or just a subset. If you always need the full user object, project ALL. If you only need UserID and Name, project INCLUDE those. This step is crucial because adding an index later is possible for GSIs, but you might incur downtime or cost if you need to backfill.

2

Create the Index (GSI or LSI)

For a GSI, use the AWS Management Console, CLI, or SDK to create the index. For an LSI, you must include it in the `CreateTable` call. Specify the index name, key schema (partition key and optional sort key), projection type, and provisioned throughput (if using provisioned mode). The index creation is asynchronous; DynamoDB will backfill the index from the base table. While the index is in CREATING status, it is not available for queries. For GSIs, you can create them on existing tables without downtime, but the backfill process consumes read capacity from the base table. Monitor the IndexStatus until it becomes ACTIVE.

3

Write Data to the Base Table

When you write an item to the base table, DynamoDB automatically updates all indexes that include that item. For a GSI, if the item has the GSI partition key attribute, it is written to the GSI. If not, the item is not included (sparse index). The write consumes WCU from both the base table and the GSI (if provisioned). For an LSI, the write consumes WCU from the base table only (shared capacity). If an update changes the GSI key attribute, DynamoDB deletes the old entry and inserts a new one in the GSI. This is an atomic operation from the perspective of the base table, but the GSI update is asynchronous.

4

Query the Index

To query a GSI, you call the `Query` API with the index name and the GSI partition key equality condition. You can also use sort key conditions (begins_with, between, >, <, etc.) if the GSI has a sort key. The query returns items from the index. If you projected only some attributes, and the query requests attributes not in the index, DynamoDB performs an additional fetch from the base table for each item (this consumes additional read capacity). For an LSI, you can set `ConsistentRead=True` for strongly consistent results. The query returns up to 1 MB of data per request; paginate using `LastEvaluatedKey`.

5

Monitor and Adjust Capacity

After deploying, monitor CloudWatch metrics for the index: `ConsumedWriteCapacityUnits`, `ThrottledWriteEvents`, `SystemErrors`. If you see throttling on the GSI, increase its write capacity or switch to on-demand mode. For GSIs, throttling can cause base table throttling. Also monitor the index's storage size. For LSIs, watch the `ItemCollectionSize` metric to ensure no partition key value exceeds 10 GB. If you approach the limit, you may need to redesign your partition key or use a GSI instead. Use AWS Compute Optimizer or DynamoDB's auto scaling to adjust capacity.

What This Looks Like on the Job

E-Commerce Order Management

A large e-commerce platform stores orders in a DynamoDB table with partition key OrderID and sort key OrderDate. The operations team needs to query orders by customer email for customer support. They create a GSI on CustomerEmail (partition key) with OrderDate as sort key, projecting all attributes. This allows support agents to quickly retrieve all orders for a customer sorted by date. The GSI is provisioned with separate read/write capacity. During Black Friday, order volume spikes, causing the GSI write capacity to be exhausted. This throttles writes to the base table, resulting in failed order placements. The solution: switch to on-demand capacity for both table and GSI, or use auto scaling with a higher minimum. Also, consider using DynamoDB Streams to replicate the data to a separate search service for complex queries.

Social Media Application

A social media app has a Posts table with partition key UserID and sort key PostTimestamp. The app needs to display a user's feed showing posts from followed users, sorted by time. This requires a query like 'get all posts where UserID is in a list of followed users'. Since DynamoDB does not support multi-table joins or IN conditions, they use a GSI on UserID (partition key) and PostTimestamp (sort key). However, each user's posts are spread across many partitions. They instead create a GSI on FollowerUserID (a computed attribute) as partition key and PostTimestamp as sort key. When a user follows another, a record is written to a Follows table, and a Lambda function writes the follow relationship to the Posts table as a separate item with a composite key. This is a common pattern called 'fan-out on write'. The GSI allows efficient querying of the feed for each user.

IoT Sensor Data

An IoT company ingests sensor data from millions of devices. Each device sends a reading every second. The base table uses DeviceID as partition key and Timestamp as sort key. They need to query all devices in a specific location (e.g., warehouse) to get aggregate metrics. They create a GSI on Location (partition key) with Timestamp as sort key. However, each location may have thousands of devices, and the GSI partition for a location can become a hot partition. To avoid this, they add a LocationShard attribute (e.g., Location + random number 0-9) as the GSI partition key, and query by scanning all shards for a location. This distributes the write load. They also use DynamoDB Streams to feed data into Amazon Timestream for time-series analytics.

Common Misconfigurations and Pitfalls

Insufficient GSI write capacity: The most common issue. When a GSI is created, its default write capacity is often set low. During peak loads, writes to the base table are throttled. Always monitor GSI throttles and set appropriate capacity.

Forgetting the 10 GB LSI limit: An application that uses a single partition key value (e.g., a tenant ID) for all items will hit this limit. Use a GSI instead or redesign the partition key.

Projecting ALL attributes on a GSI: This increases write cost and storage. If queries only need a few attributes, use KEYS_ONLY or INCLUDE.

Using LSI when GSI is needed: LSIs require the same partition key, so they don't help if you need to query by a different attribute. Many developers mistakenly use LSI for global queries.

How DVA-C02 Actually Tests This

What DVA-C02 Tests on This Topic

DVA-C02 exam objectives related to indexes fall under Domain 1: Development with AWS Services, specifically objective 1.3: 'Implement data modeling and design patterns for DynamoDB.' Key subtopics:

Choosing between GSI and LSI based on access patterns

Understanding the 10 GB limit per partition for LSIs

Knowing that LSIs must be created at table creation time

Recognizing that GSI queries are eventually consistent

Understanding that insufficient GSI write capacity throttles base table writes

Projection types and their impact on cost and performance

Common Wrong Answers and Why Candidates Choose Them

1.

'You can add an LSI to an existing table.' This is false, but many candidates think you can because GSIs can be added later. The exam tests this distinction explicitly.

2.

'GSI queries support strongly consistent reads.' False. GSIs are always eventually consistent. Candidates confuse this with LSIs or base table queries.

3.

'LSIs have separate read/write capacity.' False. LSIs share capacity with the base table. GSIs have separate capacity.

4.

'A GSI can be created with the same partition key as the base table.' This is technically possible but pointless; you would use an LSI instead. The exam may ask about the best practice.

5.

'If a GSI's write capacity is exhausted, only the GSI writes are throttled, not the base table.' False. DynamoDB throttles base table writes if the GSI write queue exceeds a threshold.

Specific Numbers, Values, and Terms

Maximum LSIs per table: 5

Maximum GSIs per table: 20 (default)

LSI partition size limit: 10 GB

LSI creation: only at table creation

GSI consistency: eventually consistent

GSI write throttling can throttle base table

Projection types: KEYS_ONLY, INCLUDE, ALL

Index statuses: CREATING, ACTIVE, UPDATING, DELETING

Query limit: 1 MB per request

Edge Cases and Exceptions

Sparse indexes: If an item lacks the GSI partition key attribute, it is not indexed. This can be used to filter items.

GSI on a table with on-demand capacity: No separate capacity management, but per-partition limits still apply (3000 RCU / 1000 WCU per partition). If a GSI partition becomes hot, you may get throttling.

LSI with sort key that is not unique: DynamoDB allows multiple items with the same sort key in an LSI? Actually, the LSI's sort key can be non-unique; the base table's primary key (partition + sort) is still unique. The LSI can have duplicate sort key values.

GSI with sort key that is also the base table sort key: This is allowed but may cause unexpected behavior if you update the sort key.

How to Eliminate Wrong Answers

If the question mentions 'strongly consistent reads on an index', eliminate any GSI option. Only LSIs support strongly consistent reads.

If the question mentions 'adding an index after table creation', eliminate LSI options. Only GSIs can be added later.

If the question mentions '10 GB limit', look for LSI. If it mentions 'separate provisioned throughput', look for GSI.

If the question describes a scenario where a query needs to filter by a different partition key, the answer must involve a GSI, not an LSI.

If the question describes base table writes being throttled despite sufficient base table capacity, suspect a GSI with insufficient write capacity.

Key Takeaways

LSIs must be created at table creation time; GSIs can be added later.

LSIs share write capacity with the base table; GSIs have separate capacity.

GSI queries are eventually consistent; LSI queries can be strongly consistent.

A GSI with insufficient write capacity can throttle base table writes.

LSI has a 10 GB limit per partition key value (base table + LSI items).

You can have up to 5 LSIs and 20 GSIs per table (default limits).

Projection types: KEYS_ONLY, INCLUDE, ALL — choose based on query needs.

Sparse indexes: items without the GSI partition key attribute are not indexed.

Query an index by specifying IndexName in the Query API call.

Monitor GSI throttling via CloudWatch metrics to avoid base table throttling.

Easy to Mix Up

These come up on the exam all the time. Here's how to tell them apart.

Global Secondary Index (GSI)

Partition key can be different from base table's partition key.

Can be created on an existing table at any time.

Has its own provisioned read/write capacity.

Queries are eventually consistent only.

No per-partition size limit.

Maximum 20 per table.

Local Secondary Index (LSI)

Partition key must be the same as base table's partition key.

Must be created at table creation time; cannot be added later.

Shares provisioned throughput with base table.

Supports strongly consistent reads (if requested).

Total size of items for a given partition key value cannot exceed 10 GB.

Maximum 5 per table.

Watch Out for These

Mistake

LSIs can be added to an existing table.

Correct

LSIs can only be created at table creation time. You cannot add an LSI to an existing table. If you need an LSI after table creation, you must create a new table and migrate data.

Mistake

GSI queries are strongly consistent by default.

Correct

GSI queries are eventually consistent. They do not support strongly consistent reads. Only queries on the base table or LSI (if specified) can be strongly consistent.

Mistake

If a GSI's write capacity is exhausted, only the GSI is affected.

Correct

If a GSI's write capacity is exhausted, DynamoDB will throttle writes to the base table as well, because the GSI write queue backs up. This is a common cause of unexpected throttling.

Mistake

LSIs have their own provisioned read/write capacity separate from the base table.

Correct

LSIs share the provisioned throughput of the base table. They do not have separate capacity settings. GSIs have their own provisioned throughput.

Mistake

You can query a GSI without specifying the index name.

Correct

To query a GSI, you must explicitly specify the index name in the Query API call. Otherwise, DynamoDB queries the base table.

Do You Actually Know This?

Reveal each answer, then mark whether you got it right. Score 60%+ to unlock the next chapter.

Frequently Asked Questions

Can I add a local secondary index to an existing DynamoDB table?

No, you cannot add an LSI to an existing table. LSIs must be specified at table creation time. If you need an LSI on an existing table, you must create a new table with the LSI and migrate your data. In contrast, GSIs can be added to existing tables at any time.

What happens if a GSI's write capacity is exceeded?

If a GSI's write capacity is exceeded, DynamoDB queues the pending writes. If the queue grows too large (up to 15 minutes), DynamoDB begins throttling writes to the base table, even if the base table has sufficient capacity. This is because the GSI cannot keep up. To avoid this, ensure the GSI has adequate write capacity or use on-demand capacity.

Can I query a GSI with strongly consistent reads?

No, GSIs do not support strongly consistent reads. All GSI queries are eventually consistent. If you need strongly consistent reads, you must query the base table or an LSI (with ConsistentRead=True). This is a fundamental limitation of GSIs.

What is the 10 GB limit for LSIs?

For a given partition key value, the total size of all items in the base table plus all items in all LSIs for that partition key value cannot exceed 10 GB. If you try to write an item that would exceed this limit, you get an ItemCollectionSizeLimitExceededException. This limit does not apply to GSIs.

Can I have multiple GSIs with the same partition key as the base table?

Yes, you can create a GSI with the same partition key as the base table. However, this is often unnecessary because you could use an LSI instead, which is more efficient (shared capacity, strong consistency). The exam may test this as a design choice: use LSI if you need strong consistency and the same partition key.

How do I choose between KEYS_ONLY, INCLUDE, and ALL projection?

KEYS_ONLY projects only the index keys and base table primary key — lowest storage/write cost, but queries may need to fetch additional attributes from the base table. INCLUDE lets you specify a subset of attributes — good if you know exactly which attributes are needed. ALL projects all attributes — highest cost, but queries can be satisfied entirely from the index, avoiding extra fetches. Choose based on your query patterns and cost tolerance.

What is a sparse index?

A sparse index is a GSI where not all items in the base table have the GSI partition key attribute. Items that lack the attribute are not included in the index. This can be used to filter items — for example, only index items that have a 'status' attribute set to 'active'. Sparse indexes reduce storage and write costs.

Terms Worth Knowing

Ready to put this to the test?

You've just covered DynamoDB Global and Local Secondary Indexes — now see how well it sticks with free DVA-C02 practice questions. Full explanations included, no account needed.

Done with this chapter?