DynamoDB indexes: global and local secondary indexes

Written by Rafal Wilinski
Last updated: October 23rd, 2026
DynamoDB secondary indexes let you query a table through alternate keys. A global secondary index (GSI) can use a different partition key and supports eventually consistent reads; a local secondary index (LSI) retains the table's partition key and can support strongly consistent reads. Both store copies of selected attributes, add write and storage costs, and allow duplicate index key values.
GSI vs LSI at a glance
| Property | Global secondary index (GSI) | Local secondary index (LSI) |
|---|---|---|
| Partition key | Can differ from the table; up to 4 attributes | Same single partition-key attribute as the table |
| Sort key | Optional; up to 4 attributes | Required alternate sort-key attribute |
| Base-table schema | Simple or composite primary key | Composite primary key required |
| When created | With the table or later | Only with the table |
| Independent deletion | Yes | No |
| Read consistency | Eventual only | Eventual or strong |
| Provisioned throughput | Separate read/write capacity | Shares the table's capacity |
| Capacity mode | Inherits the table's on-demand or provisioned mode | Uses the table's mode |
| Item-collection size | No LSI-style 10 GB limit | 10 GB per partition-key value, table plus LSI entries |
| Attributes not projected | Fetch separately from the base table | Can fetch from the table, with extra read cost and latency |
| Number per table | 20 by default; quota increase can be requested | Up to 5 |
Use a GSI when you need another partitioning dimension or need to add an access pattern to an existing table. Consider an LSI when you need another sort order within the same collection and strongly consistent index reads. The LSI size constraint and creation-time decision are part of that choice, not reasons to rule LSIs out universally.
The AWS quotas page lists the current index limits. The base table's primary key remains its unique item identifier regardless of how many indexes you add.
DynamoDB local secondary index (LSI)
Suppose Orders has partition key customerId and sort key orderId. An LSI with customerId and createdAt returns one customer's orders by date instead of by order ID. Each index entry also includes the base-table primary key, so you can identify the original order.
Declare the LSI in CreateTable. You cannot add it later, remove it independently or change its key schema in place. If you discover that requirement after creating the table, use a GSI where eventual consistency is acceptable, or migrate to a new table.
The 10 GB limit applies to an item collection: all base-table items with the same partition-key value, plus their entries in all LSIs. It is not a limit on the whole table or on each individual item. A large customer with an unbounded order history can reach the limit even when other customers have tiny collections. Use ReturnItemCollectionMetrics: "SIZE" on supported writes to observe estimated collection size.
LSI reads and maintenance use the table's capacity. If you request an attribute that was not projected, DynamoDB can fetch it from the table, but that adds work and latency. Project frequently returned fields when that tradeoff is worthwhile. AWS documents these behaviors in its LSI guide.
DynamoDB global secondary index (GSI)
On the same Orders table, a GSI keyed by sellerId and createdAt answers “orders for this seller.” The base-table query still answers “orders for this customer.” That is a new access path, not a new source of truth: applications write the table and DynamoDB maintains the index.
GSI keys do not have to be unique. Two orders can share a seller and creation timestamp and both appear in the index. If their relative ordering matters, include a deterministic tie-breaker in the sort-key design. An index on email likewise does not enforce a unique-email constraint.
GSIs are eventually consistent. Setting ConsistentRead: true on a GSI query fails; it does not make the index fresher. For a read immediately after a write, use the known base-table primary key with a strongly consistent table read. Querying a GSI and then strongly reading its results cannot recover an item that has not appeared in the index yet.
GSIs have their own provisioned capacity, but write throttling is not isolated from the base table. If an affected index cannot keep up with propagated writes, the original table writes can be throttled. A low-cardinality status index can create this back-pressure even when table capacity looks healthy.
Multi-attribute GSI keys
GSIs support up to four attributes in the partition key and four in the sort key. You can use native attributes such as sellerId and marketplace together rather than maintaining a concatenated sellerMarketplace string.
A query must specify equality for every partition-key attribute. Sort-key conditions follow the declared attributes from left to right without gaps; an inequality or prefix condition must be last. This does not turn the index into arbitrary filtering across every column. Base-table and LSI key schemas retain their existing single-attribute components. See the GSI implementation guide for creation, multi-attribute examples and deployment checks.
Projections: which attributes an index stores
| Projection | Contents | Useful when |
|---|---|---|
KEYS_ONLY | Index keys and base-table primary key | You need identifiers or will read the original item separately |
INCLUDE | Keys plus named non-key attributes | The query returns a small, stable subset of fields |
ALL | All attributes from the indexed item | The query needs the complete item and duplication is acceptable |
With INCLUDE, each index can list up to 20 non-key attributes, and the sum of NonKeyAttributes across all secondary indexes cannot exceed 100. Projecting the same attribute into two indexes counts twice. That quota does not apply to ALL or KEYS_ONLY.
A GSI query can only return attributes stored in that GSI. It cannot automatically fetch missing fields from the table. An LSI can do that fetch, with additional cost. Also distinguish an index's stored projection from a request's ProjectionExpression: returning fewer attributes from a read does not by itself reduce the capacity charged for the data read.
Sparse index
An index is sparse when only some table items contain all of its required index key attributes. For a GSI keyed by sellerId and openOrderAt, write openOrderAt only on open orders. Remove it when an order closes, and the entry disappears from the index after propagation.
A partition-key-only GSI can also be sparse: omit its partition-key attribute from excluded items. The attribute must be absent, not an empty String or null. If you use a Boolean flag such as isOpen, it cannot itself be a key; use a supported scalar attribute representing the indexed collection.
Sparse indexes can replace repeated queries followed by filters when the subset has a useful query key. They do not make contains, arbitrary substring matching or an “attribute exists” condition valid partition-key queries. You still supply partition-key equality to Query.
Inverted index
An inverted index swaps the roles of a table's partition and sort keys. If a table stores edges as pk = USER#42 and sk = GROUP#7, querying the table lists a user's groups. A GSI keyed by sk and then pk lists the group's users.
This pattern fits memberships, friendships and other many-to-many relationships. It is not a full-text search engine's inverted index. Both directions still use DynamoDB key queries, and the reverse direction is eventually consistent because it is a GSI.
Creating a GSI on an existing table
This AWS CLI v2 example assumes an existing on-demand Orders table containing String attributes sellerId and createdAt. It adds a narrowly projected seller index:
For a provisioned table, also provide the index's ProvisionedThroughput. DynamoDB allocates resources, backfills qualifying existing items, and incorporates concurrent changes. The table stays available, but a skewed new index can affect table writes. Test the data distribution and schema before starting a production build.
Use OnlineIndexPercentageProgress and table write-throttling metrics to monitor progress and impact. Check that the specific index becomes ACTIVE before deploying queries against it. A table existing or being active is not sufficient proof that its new index is ready. The online index operations guide documents the phases and restrictions.
Querying the index with SDK v3 and boto3
After BySeller is active, query the same access pattern in JavaScript:
The boto3 resource interface supports this ordinary key query:
These examples return a page, not necessarily every seller order. Pass LastEvaluatedKey back as ExclusiveStartKey to continue. Neither GetItem nor BatchGetItem accepts IndexName; use the returned table keys for subsequent item reads.
What DynamoDB indexes cost
An index stores key attributes, projected values and per-entry storage overhead. Every affected GSI adds its own write work. A new base-table item that produces one entry in each of four GSIs requires a base-table write plus four index writes, not four writes total. Actual capacity depends on the separate item and index-entry sizes.
Updating only an unprojected non-key attribute does not change a GSI entry and consumes no write capacity for that index. Changing an indexed key value generally removes the old entry and inserts the new one. Narrow projections and sparse membership can therefore reduce ongoing costs.
In provisioned mode, you pay for configured index capacity. In on-demand mode, you pay for index requests. In both modes, storage is additional. Use the DynamoDB pricing calculator with the projected entry sizes and access rates, rather than assuming a flat percentage for indexes.
Choosing and operating indexes
Document a real query for every index, including the values callers know, required sort order, consistency and returned fields. Then check distribution, projection size and expected writes. A status-only GSI with heavy traffic may need a tenant dimension or sharding; adding read capacity will not repair concentrated writes.
Monitor index and table metrics together. Index reads use index capacity, but propagated writes connect their availability. If using a GUI, Dynobase table management can create or remove GSIs and show backfill progress. Keep the intended schema in infrastructure code so deployments and application queries agree.
Frequently Asked Questions
What is the difference between GSI and LSI?
How many secondary indexes can a DynamoDB table have?
Are DynamoDB secondary index keys unique?
Can a GSI throttle writes to the table?
Can I add an index to an existing DynamoDB table?
Can I use GetItem on a DynamoDB index?
Dynobase is a Professional GUI Client for DynamoDB
Start your 7-day free trial today