This chapter dives deep into DynamoDB Global Secondary Indexes (GSIs) and Local Secondary Indexes (LSIs), two of the most powerful features for optimizing query performance in AWS DynamoDB. On the SAA-C03 exam, approximately 10-15% of questions touch on DynamoDB indexing, often in the context of designing high-performance, cost-effective data access patterns. Understanding when and how to use GSIs versus LSIs, along with their trade-offs in consistency, throughput, and storage, is critical for scenario-based questions that ask you to choose the best index strategy for a given workload.
Jump to a section
Imagine a massive library with millions of books, each having a unique ID (primary key) and attributes like title, author, genre, and publication year. The main card catalog is organized by book ID—you can quickly find a book if you know its ID. But what if you want to find all books by a specific author? Without an index, you'd have to walk through every shelf (full table scan), which is slow and costly. A Global Secondary Index (GSI) is like building a separate card catalog sorted by author. This new catalog has its own cards (index items) that point back to the main book locations. You can have multiple such catalogs (up to 20) for different attributes like genre or year. Critically, this new catalog can have a different sort order—e.g., sorted by publication date within each author. A Local Secondary Index (LSI) is different: it's like adding a second set of tabs within the same card catalog drawer, keeping the same partition key (book ID) but allowing a different sort key, like sorting by title within each ID drawer. You can only create LSIs when you build the main catalog (table creation), and they share the read/write capacity of the main catalog. GSIs, however, can be added later and have their own provisioned throughput. Both allow efficient queries without scanning, but GSIs are eventually consistent by default, while LSIs offer strong consistency within the same partition.
What are DynamoDB Secondary Indexes?
A DynamoDB table is a collection of items, each uniquely identified by a primary key. The primary key can be a simple partition key (hash key) or a composite partition key + sort key (range key). Without indexes, you can only query items using the primary key or scan the entire table—both inefficient for non-key attributes. Secondary indexes provide alternative access patterns by allowing queries on other attributes.
There are two types: Global Secondary Index (GSI) and Local Secondary Index (LSI). A GSI has a different partition key and optionally a different sort key from the base table. An LSI uses the same partition key as the base table but a different sort key.
How GSIs Work Internally
When you create a GSI, DynamoDB automatically replicates data from the base table into the index. The index is a separate data structure with its own partition key and sort key. Each item in the index is a projection of the base table item—you choose which attributes to copy (KEYS_ONLY, INCLUDE, or ALL). The index is partitioned by its own partition key, which can be any scalar attribute (String, Number, Binary).
When you write to the base table, DynamoDB asynchronously propagates the change to all GSIs. This means GSIs are eventually consistent—there is a propagation delay, typically sub-second but not guaranteed. You cannot achieve strong consistent reads from a GSI. The write capacity consumed by a GSI write is separate from the base table's write capacity. Each GSI has its own provisioned read/write capacity or can use on-demand mode.
Queries against a GSI work just like table queries: you specify the partition key equality condition and optionally a sort key condition (begins_with, between, >, <, etc.). The query returns items from the index, not the base table. If you projected all attributes, the response contains full items; otherwise, you may need to fetch missing attributes from the base table (costing additional read capacity).
How LSIs Work Internally
An LSI is created at table creation time and cannot be added later. It shares the same partition key as the base table but uses a different sort key. The LSI is stored in the same partition as the base table items—actually, each partition stores items sorted by sort key, and the LSI adds an additional sort order within that partition. This means the LSI is co-located with the base table data, so reads can be strongly consistent (if you choose). LSI writes consume the same write capacity as the base table write—no additional capacity is needed. However, LSI storage counts against the table's storage limit (10 GB per partition key value).
Key Components and Defaults
GSI: Up to 20 per table (default limit, can be increased). Can be created at any time. Index key attributes can be any scalar attribute. Supports eventually consistent reads only. Has its own provisioned throughput.
LSI: Up to 5 per table. Must be created when the table is created. Index sort key can be any scalar attribute. Supports both eventually consistent and strongly consistent reads. Shares throughput with base table.
Projection: Determines which attributes are copied to the index. KEYS_ONLY (only index key and primary key), INCLUDE (specified attributes + keys), ALL (all attributes). Default is KEYS_ONLY for GSIs and ALL for LSIs (though you can specify).
Consistency: GSI is eventually consistent only. LSI supports strongly consistent reads within the same partition.
Write Capacity: GSI writes consume separate WCU. LSI writes consume the base table's WCU (no additional cost).
Storage: GSI storage is additional. LSI storage is within the base table's 10 GB partition limit.
Configuration and Verification
To create a GSI using the 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},{AttributeName=Title,KeyType=RANGE}],Projection={ProjectionType=ALL},ProvisionedThroughput={ReadCapacityUnits=5,WriteCapacityUnits=5}}'To create an LSI during table creation:
aws dynamodb create-table --table-name mytable --attribute-definitions AttributeName=PK,AttributeType=S AttributeName=SK,AttributeType=S --key-schema AttributeName=PK,KeyType=HASH AttributeName=SK,KeyType=RANGE --local-secondary-indexes IndexName=LSI1,KeySchema=[{AttributeName=PK,KeyType=HASH},{AttributeName=Date,KeyType=RANGE}],Projection={ProjectionType=ALL}To verify indexes:
aws dynamodb describe-table --table-name mytableLook for GlobalSecondaryIndexes and LocalSecondaryIndexes in the output.
Interaction with Related Technologies
DynamoDB Streams: When you enable streams on a base table, changes to GSIs are not streamed separately. Only changes to the base table are captured. If you need to react to index changes, you must handle the base table stream.
DAX (DynamoDB Accelerator): DAX can cache GSI queries, but it only caches eventually consistent reads. Strongly consistent reads (LSI) are not cached.
TTL (Time to Live): TTL deletions on the base table also delete corresponding index items. However, if the index item's partition key is different from the base table's, the deletion may not be immediate due to asynchronous propagation.
Global Tables: GSIs are replicated along with the base table. Each replica region has its own GSI. LSIs are local to each region and must be created on each replica table.
Performance Considerations
GSI Write Throttling: If a GSI's write capacity is insufficient, writes to the base table will be throttled—even if the base table has enough capacity. This is a common exam trap.
GSI Query vs Scan: Always prefer query over scan. A query on a GSI can be just as efficient as a query on the base table if you use the index's partition key.
LSI and Hot Partitions: Since LSI shares the partition key, a hot partition (too many writes to one partition key value) can throttle both base table and LSI writes. GSI can help distribute load across different partition keys.
Index Size: Large indexes consume storage and increase write costs. Project only necessary attributes to reduce overhead.
Exam-Relevant Defaults and Limits
Maximum GSI per table: 20 (soft limit, can be raised).
Maximum LSI per table: 5.
GSI partition key size: max 2048 bytes.
GSI sort key size: max 1024 bytes.
LSI sort key size: max 1024 bytes.
GSI write capacity per partition: 1000 WCU (if using provisioned).
GSI read capacity per partition: 3000 RCU.
Base table item size: 400 KB max.
Index item size: 400 KB max (includes projected attributes).
Each partition key value in a table can have up to 10 GB of data (including LSI storage).
Common Exam Scenarios
Scenario: You need to query by a non-key attribute. Solution: Create a GSI with that attribute as partition key.
Scenario: You need to query by a non-key attribute but with strong consistency. Solution: This is not possible with GSI. You must use the base table with a composite key or accept eventual consistency.
Scenario: You need to query by a different sort key within the same partition. Solution: Use an LSI with that sort key.
Scenario: You need to add an index after table creation. Solution: Only GSI can be added later; LSI must be created at table creation.
Scenario: You are concerned about write costs. Solution: LSIs do not consume additional write capacity, while GSIs do.
Identify Access Patterns
Before creating any index, analyze your application's query requirements. Determine which attributes you need to query by (partition key) and optionally sort by (sort key). For each access pattern, note whether strong consistency is required. This step determines whether you need a GSI (no strong consistency) or an LSI (strong consistency possible) or perhaps the base table itself. For example, if you need to find all orders for a customer (partition key = CustomerID) sorted by order date (sort key), and the base table already uses CustomerID as partition key but sorted by OrderID, you would create an LSI with sort key OrderDate. If you need to query by ProductID (different partition key), you need a GSI.
Choose Index Type: GSI or LSI
If the new access pattern uses a different partition key than the base table, you must use a GSI. If it uses the same partition key but a different sort key, you can use either an LSI or a GSI. However, LSIs are limited to 5 per table and must be created at table creation. GSIs can be added later, support up to 20, and have separate throughput. For strong consistency, you must use an LSI (or the base table) because GSIs only support eventual consistency. Also, LSIs share the base table's write capacity, while GSIs consume additional write capacity. Consider cost: LSIs are cheaper for writes but may lead to hot partitions.
Define Index Schema and Projection
For the selected index type, define the index key schema: partition key (HASH) and optional sort key (RANGE). The key attributes must be scalar types (String, Number, Binary). Then choose the projection type: KEYS_ONLY (only index keys and primary key from base table), INCLUDE (specified attributes plus keys), or ALL (all attributes). Projecting all attributes simplifies queries but increases storage and write costs. Projecting only keys reduces overhead but may require additional 'fetch' queries to the base table for missing attributes, costing extra read capacity. For example, if your query always needs the 'Price' attribute, include it in the projection.
Configure Throughput (Provisioned or On-Demand)
For a GSI, you must specify read and write capacity units if using provisioned mode. The GSI's write capacity is consumed separately from the base table. If the GSI's write capacity is insufficient, base table writes will be throttled (this is a critical exam point). For LSIs, no separate throughput is needed—they use the base table's capacity. If using on-demand mode, throughput scales automatically for both base table and GSIs. However, on-demand can be more expensive for predictable workloads. Consider using provisioned with auto-scaling for cost efficiency.
Create Index and Verify
Create the index using the AWS Management Console, CLI, or SDK. For LSIs, you must create them during table creation. For GSIs, you can add them to an existing table using the UpdateTable operation. After creation, the index status will be 'ACTIVE' when ready. Use `describe-table` to verify the index configuration. Monitor the index's throttling metrics (WriteThrottleEvents, ReadThrottleEvents) in CloudWatch. Test queries against the index to ensure they return expected results. Note that GSI creation can take minutes to hours depending on table size, as DynamoDB backfills data from the base table.
Enterprise Scenario 1: E-commerce Order Management
A large e-commerce platform stores orders in a DynamoDB table with primary key (OrderID). The application needs to query orders by CustomerID (partition key) sorted by OrderDate (sort key). The base table does not have CustomerID as partition key. The solution: create a GSI with CustomerID as partition key and OrderDate as sort key, projecting all attributes. This allows efficient queries for a customer's order history. In production, the table handles 10,000 writes/second, and the GSI must be provisioned with sufficient write capacity to avoid throttling. A common mistake is underestimating GSI write capacity—if the GSI write capacity is 5000 WCU but base table writes are 8000 WCU, the base table will be throttled. The team set up auto-scaling on the GSI to match base table write patterns. They also chose to project only frequently accessed attributes (OrderStatus, TotalAmount) to reduce storage costs.
Enterprise Scenario 2: IoT Sensor Data
A manufacturing company collects sensor readings from thousands of devices. The base table uses (DeviceID, Timestamp) as primary key. They need to query by sensor type (e.g., temperature) across all devices within a time range. This requires a different partition key (SensorType). They create a GSI with SensorType as partition key and Timestamp as sort key. However, they also need to occasionally query all readings for a specific device sorted by a different attribute, like reading value. For this, they create an LSI on the base table with sort key = ReadingValue. The LSI is created at table creation time. The team must plan ahead because LSIs cannot be added later. They also note that the LSI shares the base table's write capacity, which is acceptable since write volume is moderate. A misconfiguration: they initially projected all attributes to the LSI, causing storage to exceed the 10 GB per partition limit for devices with many readings. They changed to KEYS_ONLY and fetch missing attributes from the base table when needed.
Scenario 3: Gaming Leaderboard
A mobile game tracks player scores in a table with (PlayerID, GameID) as primary key. They need a global leaderboard sorted by score. They create a GSI with a constant partition key (e.g., 'leaderboard') and Score as sort key. This ensures all items land in one partition, allowing efficient range queries. However, this creates a hot partition—all writes and reads go to the same partition. With high write volume (100,000 writes/second), the single partition cannot handle the load. They must redesign: use a sharded GSI where partition key is a combination of score range (e.g., score bucket) to distribute writes. For reads, they query each shard and merge results. This is a common exam scenario—avoid single partition GSIs for high-write workloads.
The SAA-C03 exam tests DynamoDB indexing primarily under objective 3.6 (Design high-performance architectures) and 3.7 (Design cost-optimized architectures). Expect scenario-based questions where you must choose the right index type or identify why a design fails.
Top 3 Wrong Answers Candidates Choose:
Choosing LSI when a GSI is needed: Candidates see that a query requires a different partition key and incorrectly think an LSI can use a different partition key. Reality: LSI must use the same partition key as the base table. If the query needs a different partition key, it must be a GSI.
Assuming GSIs support strongly consistent reads: Many candidates think all DynamoDB reads can be strongly consistent. Reality: GSIs only support eventually consistent reads. If strong consistency is required, you must use the base table or an LSI.
Ignoring GSI write throttling affecting base table: Candidates provision sufficient base table write capacity but forget the GSI. When the GSI write capacity is exceeded, base table writes are throttled. This is a classic exam trap—always check GSI write capacity.
Specific Numbers and Terms on the Exam:
Maximum 20 GSIs per table (default).
Maximum 5 LSIs per table.
LSIs must be created at table creation time.
GSIs can be added or modified after table creation.
GSI reads are eventually consistent only.
LSI reads can be strongly consistent.
LSI shares base table's provisioned throughput; GSI has its own.
Projection types: KEYS_ONLY, INCLUDE, ALL.
Each partition key value can have up to 10 GB of data.
Item size limit: 400 KB (including all projected attributes in indexes).
Edge Cases the Exam Loves:
GSI with sparse index: If many items don't have the GSI partition key attribute, they are not included in the index. This can be used to query only items that have that attribute.
LSI and item collections: Within a partition, items are grouped by partition key. LSI allows querying within that group with a different sort order.
Throttling on GSI creation: When creating a GSI on a large table, the backfill process consumes read capacity from the base table and write capacity on the GSI. If the base table has insufficient read capacity, the creation may throttle.
How to Eliminate Wrong Answers:
If the question mentions a different partition key, eliminate LSI options.
If strong consistency is required, eliminate GSI options.
If the index needs to be added later, eliminate LSI options.
If write capacity is a concern, consider LSI (no additional write cost) but note the 10 GB partition limit.
If the query needs to sort by a different attribute within the same partition, LSI is a good fit.
GSIs allow queries on any attribute as partition key; LSIs only allow a different sort key within the same partition key.
LSIs must be created when the table is created; GSIs can be added anytime.
GSIs only support eventually consistent reads; LSIs support strongly consistent reads.
GSI write throttling can throttle base table writes—always provision sufficient GSI write capacity.
LSIs share base table write capacity and storage (10 GB per partition key limit).
Maximum 20 GSIs and 5 LSIs per table (default limits).
Projection type (KEYS_ONLY, INCLUDE, ALL) affects storage cost and query performance.
These come up on the exam all the time. Here's how to tell them apart.
Global Secondary Index (GSI)
Different partition key from base table
Can be created after table creation
Up to 20 per table
Eventually consistent reads only
Has its own provisioned read/write capacity
Local Secondary Index (LSI)
Same partition key as base table
Must be created at table creation
Up to 5 per table
Supports strongly consistent reads
Shares base table's provisioned throughput
Mistake
LSIs can be added to an existing table.
Correct
LSIs must be defined at table creation time. You cannot add an LSI after the table is created. Only GSIs can be added later.
Mistake
GSIs support strongly consistent reads.
Correct
GSIs only support eventually consistent reads. Strongly consistent reads are only available on the base table and LSIs.
Mistake
GSI writes do not affect base table write capacity.
Correct
If a GSI's write capacity is exceeded, the base table write will be throttled. The base table and all its GSIs must have sufficient write capacity together.
Mistake
You can have up to 5 GSIs per table.
Correct
The default limit is 20 GSIs and 5 LSIs per table. Candidates often confuse the numbers.
Mistake
LSIs and GSIs both require separate provisioned throughput.
Correct
LSIs share the base table's provisioned throughput. Only GSIs have their own separate provisioned read/write capacity.
Reveal each answer, then mark whether you got it right. Score 60%+ to unlock the next chapter.
No, you cannot add an LSI after the table is created. LSIs must be defined at table creation time. If you need an LSI, you must create a new table with the LSI defined and migrate your data. In contrast, GSIs can be added to an existing table using the UpdateTable operation.
GSIs only support eventually consistent reads. LSIs support both eventually consistent and strongly consistent reads (when using ConsistentRead=true). Strong consistency ensures that you read the most recent write, but it may have higher latency and consume more read capacity units (RCU). If your application requires strong consistency, use an LSI or the base table.
By default, you can have up to 20 GSIs and up to 5 LSIs per table. These limits can be increased by contacting AWS Support. Note that LSIs count against the 5 limit regardless of the table's size.
No, a GSI has its own provisioned write capacity (or uses on-demand). However, if the GSI's write capacity is insufficient, writes to the base table will be throttled. This is because DynamoDB cannot complete the write without also writing to the index. Therefore, you must ensure the GSI has enough write capacity to handle the base table's write rate.
No, GSIs only support eventually consistent reads. If you need strongly consistent reads, you must query the base table or an LSI. This is a common exam trap: candidates often assume GSIs support strong consistency.
The maximum item size in a DynamoDB index is 400 KB, the same as the base table. This includes all projected attributes. If an item exceeds 400 KB when projected to an index, DynamoDB will not store it in that index, and the write will fail.
Use KEYS_ONLY projection when you only need to retrieve items by their primary key and can afford an additional query to the base table to get other attributes. This reduces storage costs and write capacity consumption. However, each query that needs additional attributes will require a separate GetItem or Query on the base table, increasing read costs.
You've just covered DynamoDB GSI and LSI — now see how well it sticks with free SAA-C03 practice questions. Full explanations included, no account needed.
Done with this chapter?