Index Performance Optimization
Complete the full lesson to earn 25 points — 50 with Pro
Work through each section, then tap “Mark as Complete” on the last one.
✦ Skip the page breaks, the wait, and see fewer ads — read each lesson on a single page with Pro
Index Performance Optimization in Azure Cosmos DB
Introduction: The Foundation of Database Performance
When you work with Azure Cosmos DB, you are interacting with a globally distributed, multi-model database service designed for high availability and low latency. However, even the most powerful hardware and distributed architecture cannot compensate for inefficient data retrieval patterns. At the heart of every read operation in Cosmos DB lies the indexing engine. Understanding how to optimize this engine is the single most effective way to control your Request Unit (RU) consumption, reduce latency, and ensure your application scales predictably as your data volume grows.
An index is essentially a map that the database uses to locate data without having to scan every single document in a collection. By default, Cosmos DB indexes every property of every item, which provides a great "out of the box" experience but can become a significant performance bottleneck as your data model increases in complexity. If your indexing strategy is not aligned with your application's query patterns, you will find yourself consuming excessive RUs, facing throttled requests, and observing slow response times that frustrate end-users.
In this lesson, we will peel back the layers of the Cosmos DB indexing engine. We will explore how indexing policies work, how to customize them to fit specific workload requirements, and the trade-offs involved in balancing write performance against read efficiency. By the end of this guide, you will be equipped to design indexing strategies that support high-performance applications while keeping operational costs under control.
Understanding the Cosmos DB Indexing Engine
To optimize your indexing strategy, you must first understand what the indexing engine is actually doing. Every time you insert, update, or delete a document, the Cosmos DB engine automatically updates the index. This means that while indexing makes reads faster, it introduces a cost during write operations. Every property included in the index adds a small amount of overhead to the ingestion process.
By default, Cosmos DB uses a "Consistent" indexing mode. This ensures that when you perform a point read or a query, the results are always consistent with the most recent write. This is the gold standard for many applications, but it requires that the index be updated synchronously with the write operation. If you have a write-heavy workload, this synchronous update can significantly increase the cost of your write operations.
The Components of an Indexing Policy
An indexing policy is defined as a JSON document associated with each container. It dictates which paths are included or excluded from the index, the type of index (e.g., range, hash, or spatial), and the indexing mode. By default, the policy looks like this:
{
"indexingMode": "consistent",
"automatic": true,
"includedPaths": [
{
"path": "/*"
}
],
"excludedPaths": [
{
"path": "/\"_etag\"/?"
}
]
}
This default policy is "all-inclusive." It indexes every path except for the system-generated _etag property. While convenient for development, it is rarely the most efficient choice for a production environment, especially when you have large documents with many nested properties that are never queried.
Callout: Consistent vs. Lazy Indexing In older iterations of database systems, "lazy" indexing was common, where the index was updated asynchronously. In modern Cosmos DB, the "Lazy" indexing mode is deprecated. You now choose between "Consistent" (default) and "None." If you need to disable indexing for specific collections to save on write costs, you must set the indexing mode to "None." This is useful for temporary data or collections where you only ever perform point reads by ID.
Practical Strategies for Index Optimization
Optimizing your indexing strategy starts with identifying your query patterns. You cannot optimize what you do not understand. Before changing your indexing policy, you should use the Azure Portal's "Metrics" blade or the Cosmos DB Query Stats to see which queries are consuming the most RUs and whether they are performing full container scans.
1. The Principle of Least Inclusion
The most important rule in index optimization is to index only what you need. If your application queries by category and price, there is no reason to index a large description field or an array of tags that you only retrieve via point reads. By excluding unnecessary paths, you reduce the write overhead and the storage cost of the index itself.
2. Utilizing Range Indexes for Comparisons
By default, Cosmos DB creates range indexes for all paths. Range indexes are necessary for equality comparisons (=) as well as range comparisons (>, <, >=, <=). If you know that you will only ever query a specific property using equality, you might consider using a hash index if it were available (though Cosmos DB primarily relies on range indexes for most scenarios).
3. Handling Large Documents and Arrays
Arrays present a unique challenge in Cosmos DB indexing. When you index an array, the engine creates an entry for every single element within that array. If you have a document with an array containing thousands of items, indexing that path can explode the size of your index and lead to massive RU spikes during writes. Always exclude large arrays from your indexing policy unless you absolutely need to query the contents of those arrays.
Implementing Custom Indexing Policies
Let’s walk through a scenario where we want to optimize a container that stores product data. The documents look like this:
{
"id": "prod-123",
"name": "Wireless Mouse",
"category": "Electronics",
"price": 29.99,
"internal_metadata": {
"warehouse_code": "WH-01",
"last_scanned": "2023-10-01"
},
"tags": ["peripheral", "office", "wireless"]
}
If our application only ever searches by category and price, we can significantly optimize the index by excluding everything else.
Step-by-Step: Updating the Indexing Policy
- Navigate to your Cosmos DB account in the Azure portal.
- Select the "Data Explorer" tab and locate your container.
- Click on the "Settings" tab for that container.
- Locate the "Indexing Policy" section.
- Modify the JSON to explicitly include only the necessary paths.
{
"indexingMode": "consistent",
"automatic": true,
"includedPaths": [
{
"path": "/category/?"
},
{
"path": "/price/?"
}
],
"excludedPaths": [
{
"path": "/*"
}
]
}
Explanation of this configuration:
- We set the
excludedPathsto/*, which acts as a global "exclude all" rule. - We then explicitly add the
includedPathsforcategoryandprice. - The
/?at the end of the path indicates that we want to index the value at that specific path. - Any property not explicitly added will now be ignored by the indexing engine, significantly reducing the write cost for every document update.
Warning: The "Exclude All" Trap Be very careful when using
/*in yourexcludedPaths. If you accidentally exclude a property that your application relies on for filtering, those queries will be forced to perform a full scan. A full scan is essentially a "table scan" in relational terms, and it will consume a massive amount of Request Units, likely leading to immediate performance degradation. Always test your queries in the Query Explorer after changing an index policy.
Advanced Indexing: Composite Indexes
Composite indexes are required when you have queries that filter by multiple properties or sort by multiple properties. A standard index handles single-path queries efficiently, but if you have a query like SELECT * FROM c WHERE c.category = 'Electronics' ORDER BY c.price DESC, the engine needs a composite index to perform that operation efficiently.
Without a composite index, the engine must fetch all documents matching the category, load them into memory, and then perform an in-memory sort. This is extremely expensive in terms of RUs.
Configuring a Composite Index
To support the query above, you would update your policy to include a compositeIndexes block:
"compositeIndexes": [
[
{
"path": "/category",
"order": "ascending"
},
{
"path": "/price",
"order": "descending"
}
]
]
When you define a composite index, the order of the paths matters. The query must match the order defined in the index. If you define the index as category then price, a query filtering by price then category will not be able to use that specific composite index.
Best Practices and Industry Standards
To maintain a healthy Cosmos DB indexing strategy, adhere to the following guidelines:
- Monitor RU Consumption: If you see high RU costs for simple queries, check the "Query Stats" to see if the query is using the index or performing a scan.
- Use Point Reads Whenever Possible: If you are retrieving a single document by its
idandpartition key, an index is not strictly required. Point reads are the most efficient way to interact with Cosmos DB and are much cheaper than queries. - Avoid Over-Indexing: Do not index properties that you never use in
WHERE,ORDER BY, orJOINclauses. Every indexed property is a tax on your write performance. - Test in Development: Never deploy an indexing policy change directly to production without testing it against a representative dataset. Use the Azure Cosmos DB Emulator to simulate production workloads.
- Keep Documents Flat: Wherever possible, avoid deeply nested JSON structures. Deep nesting makes indexing policies more complex to manage and can lead to accidental indexing of large, unnecessary data blobs.
Comparison: Indexing Options
| Feature | Consistent Indexing | No Indexing |
|---|---|---|
| Write Performance | Slower (due to overhead) | Faster |
| Read Performance | High (for indexed queries) | Very Slow (full scans) |
| Consistency | Strong/Bounded Staleness | N/A |
| Best Use Case | Production apps with queries | Temp data or ID-only lookups |
Common Pitfalls and How to Avoid Them
Pitfall 1: Indexing Everything
Many developers leave the default policy enabled because it is "safe." However, as your database grows to millions of items, the index itself can become larger than the data. This increases storage costs and slows down the indexing engine as it tries to maintain a massive index structure.
The Fix: Periodically audit your queries. If a property isn't being queried, remove it from the includedPaths or use the excludedPaths pattern to keep the index lean.
Pitfall 2: Forgetting the Partition Key
The partition key is automatically indexed. You cannot exclude it. However, developers often forget that their queries should always include the partition key to be as efficient as possible. A cross-partition query is inherently more expensive than a single-partition query, regardless of how well your indexing policy is tuned.
The Fix: Always design your data model such that your most common queries include the partition key.
Pitfall 3: Ignoring "Order By" Requirements
If you have a query that returns results in a specific order, you must ensure that an index exists that supports that sort order. If you try to sort by a property that is not indexed, the query will fail or perform a scan.
The Fix: If you need to sort by a specific property, ensure it is included in your index. If you need to sort by multiple properties, you must define a composite index.
The Role of TTL (Time to Live) and Indexing
Time to Live (TTL) is a feature that allows you to automatically expire documents after a set period. While TTL is primarily a data lifecycle feature, it interacts with the index. When a document expires, the index must also be updated to remove the references to that document.
If you have a high volume of documents expiring simultaneously, you may see a spike in RU consumption. This is because the background process responsible for cleaning up expired documents and updating the index is working harder. If your indexing policy is overly complex, this cleanup process becomes even more resource-intensive.
Note: If you are using TTL, ensure that your indexing policy is as lean as possible. By excluding unnecessary properties, you reduce the amount of work the cleanup process has to perform, leading to smoother resource utilization over time.
Advanced Query Optimization Techniques
Even with a perfect indexing policy, some queries can be problematic. For example, queries that use functions in the WHERE clause, such as SELECT * FROM c WHERE UPPER(c.name) = 'PRODUCT', can often bypass the index.
Why Functions Bypass the Index
The indexing engine creates entries based on the actual values stored in the document. When you wrap a property in a function like UPPER(), the database cannot look up the value in the index because the index doesn't store the "uppercased" version of your data. It only stores the raw value.
The Fix: Store the data in the format you intend to query it. If you need case-insensitive searches, store a name_lowercase property alongside the original name property and query against the name_lowercase field. This allows the index to work as intended.
Handling IN and OR Clauses
Queries using IN or OR can sometimes be inefficient. While the indexing engine can handle these, they can lead to large result sets that consume many RUs.
The Fix: Whenever possible, break OR queries into separate queries or use the IN operator carefully. If you find yourself using OR frequently, evaluate whether your data model could be denormalized to avoid the need for complex filtering.
Monitoring and Troubleshooting
How do you know if your indexing strategy is working? Cosmos DB provides excellent diagnostic tools.
- Request Charge: Every response from the Cosmos DB SDK includes a
x-ms-request-chargeheader. If this number is consistently high, your query is likely inefficient. - Query Stats: The
x-ms-document-query-metricsheader provides detailed information about how the query was executed, including the number of documents retrieved and the number of index hits. - Azure Monitor: You can set up alerts for high RU consumption. If a specific query pattern suddenly starts consuming more RUs, you will be notified before it impacts your users.
Step-by-Step: Analyzing a Slow Query
If a query is slow, follow this procedure:
- Copy the query into the Azure Portal Query Explorer.
- Run the query and click on the "Query Stats" tab.
- Look for the "Index hit ratio." A low ratio indicates that the query is scanning many documents that don't match the criteria.
- Check if the query is performing a cross-partition scan.
- Adjust the index policy or the query structure accordingly.
Future-Proofing Your Indexing Strategy
As your application evolves, your indexing needs will change. What works for a prototype will not work for a production system with millions of documents.
- Start Simple: Don't over-engineer your index on day one. Start with the default policy, but monitor your RU usage closely.
- Iterative Refinement: As you identify your core query patterns, start narrowing your index to include only those properties.
- Document Your Strategy: Keep a record of why specific paths are included or excluded. This will be invaluable when a new developer joins the team and needs to understand why certain queries are fast while others are slow.
- Stay Updated: Microsoft frequently updates the Cosmos DB engine. Keep an eye on the official documentation for new indexing features, such as improved support for spatial queries or new index types.
Key Takeaways
- Indexing is a Trade-off: Every indexed property improves read performance but increases write latency and RU consumption. Balance this based on your application's read/write ratio.
- Exclude Unnecessary Paths: The default "all-inclusive" index is rarely optimal for production. Use an "exclude-all" strategy and explicitly add only the paths you actually query.
- Composite Indexes are Essential: For queries involving multi-property filters or sorts, composite indexes are not optional; they are required to prevent expensive in-memory processing.
- Data Modeling Matters: How you structure your JSON affects index efficiency. Avoid deep nesting and store data in the format you intend to query to avoid using functions in
WHEREclauses. - Monitor to Optimize: Use the Request Charge and Query Metrics to identify inefficient queries. Never assume your indexing strategy is perfect; let the metrics guide your refinements.
- Point Reads are King: If your application can retrieve data by ID and partition key, do so. This bypasses the complexity of indexing altogether and provides the most performant access pattern.
- Test Before You Deploy: Always validate index policy changes in a non-production environment. A poorly configured index can cause immediate and significant performance degradation.
By applying these principles, you move from being a passive user of Cosmos DB to an active architect of your data layer. A well-tuned indexing strategy is the difference between a database that struggles under load and one that provides consistent, lightning-fast performance at any scale. Continue to refine your approach, monitor your metrics, and keep your data model aligned with your access patterns.
Reach the last section to complete this lesson and earn points — you're on section 1 of 10.
- Introduction to Cosmos DB Data Modeling
- Introduction to Cosmos DB Data Modeling Quiz5q
- Multiple Entity Types in Same Container
- Multiple Entity Types in Same Container Quiz5q
- Storing Related Entities in Same Document
- Storing Related Entities in Same Document Quiz5q
- Denormalizing Data Across Documents
- Denormalizing Data Across Documents Quiz5q
- Referencing Between Documents
- Referencing Between Documents Quiz5q
- Partition Keys and Document IDs
- Partition Keys and Document IDs Quiz5q
- Time to Live (TTL) Configuration
- Time to Live (TTL) Configuration Quiz5q
- Document Versioning Strategies
- Document Versioning Strategies Quiz5q
- Schema Versioning Patterns
- Schema Versioning Patterns Quiz5q
- Choosing Partition Strategies
- Choosing Partition Strategies Quiz5q
- Partition Key Selection Best Practices
- Partition Key Selection Best Practices Quiz5q
- Transactions and Partition Keys
- Transactions and Partition Keys Quiz5q
- Cross-Partition Query Costs
- Cross-Partition Query Costs Quiz5q
- Data Distribution Analysis
- Data Distribution Analysis Quiz5q
- Throughput Distribution Planning
- Throughput Distribution Planning Quiz5q
- Synthetic Partition Keys
- Synthetic Partition Keys Quiz5q
- Hierarchical Partition Keys
- Hierarchical Partition Keys Quiz5q
- Throughput and Storage Requirements
- Throughput and Storage Requirements Quiz5q
- Serverless vs Provisioned Throughput
- Serverless vs Provisioned Throughput Quiz5q
- Database-Level Provisioned Throughput
- Database-Level Provisioned Throughput Quiz5q
- Granular Scale Units
- Granular Scale Units Quiz5q
- Global Distribution Costs
- Global Distribution Costs Quiz5q
- Configuring Throughput in Portal
- Configuring Throughput in Portal Quiz5q
- Gateway vs Direct Connectivity Mode
- Gateway vs Direct Connectivity Mode Quiz5q
- Creating Database Connections
- Creating Database Connections Quiz5q
- Azure Cosmos DB Emulator
- Azure Cosmos DB Emulator Quiz5q
- Connection Error Handling
- Connection Error Handling Quiz5q
- Singleton Pattern for Clients
- Singleton Pattern for Clients Quiz5q
- Global Distribution Regions
- Global Distribution Regions Quiz5q
- Threading and Parallelism
- Threading and Parallelism Quiz5q
- Arrays and Nested Objects Queries
- Arrays and Nested Objects Queries Quiz5q
- Correlated Subqueries
- Correlated Subqueries Quiz5q
- Array and Type-Checking Functions
- Array and Type-Checking Functions Quiz5q
- Mathematical and String Functions
- Mathematical and String Functions Quiz5q
- Date Functions in Queries
- Date Functions in Queries Quiz5q
- Point Operations vs Query Operations
- Point Operations vs Query Operations Quiz5q
- CRUD Point Operations
- CRUD Point Operations Quiz5q
- Patch Operations for Updates
- Patch Operations for Updates Quiz5q
- Transactional Batch Operations
- Transactional Batch Operations Quiz5q
- Bulk Operations with SDK
- Bulk Operations with SDK Quiz5q
- Optimistic Concurrency with ETags
- Optimistic Concurrency with ETags Quiz5q
- Query Pagination and Continuation
- Query Pagination and Continuation Quiz5q
- Cosmos DB Mirroring for Fabric
- Cosmos DB Mirroring for Fabric Quiz5q
- Mirroring vs Spark Connector
- Mirroring vs Spark Connector Quiz5q
- Enabling Analytical Store
- Enabling Analytical Store Quiz5q
- Synapse Spark and SQL Queries
- Synapse Spark and SQL Queries Quiz5q
- Change Data Capture in Analytical Store
- Change Data Capture in Analytical Store Quiz5q
- Azure Functions and Event Hubs Integration
- Azure Functions and Event Hubs Integration Quiz5q
- Denormalization with Change Feed
- Denormalization with Change Feed Quiz5q
- Referential Integrity with Change Feed
- Referential Integrity with Change Feed Quiz5q
- Azure AI Search Integration
- Azure AI Search Integration Quiz5q
- Azure Functions Change Feed Trigger
- Azure Functions Change Feed Trigger Quiz5q
- Consuming Change Feed with SDK
- Consuming Change Feed with SDK Quiz5q
- Change Feed Estimator
- Change Feed Estimator Quiz5q
- Denormalization via Change Feed
- Denormalization via Change Feed Quiz5q
- Aggregation Persistence with Change Feed
- Aggregation Persistence with Change Feed Quiz5q
- Read-Heavy vs Write-Heavy Indexing
- Read-Heavy vs Write-Heavy Indexing Quiz5q
- Index Type Selection
- Index Type Selection Quiz5q
- Custom Indexing Policies
- Custom Indexing Policies Quiz5q
- Composite Index Implementation
- Composite Index Implementation Quiz5q
- Index Performance Optimization
- Index Performance Optimization Quiz5q
- Response Status Codes and Metrics
- Response Status Codes and Metrics Quiz5q
- Normalized RU Consumption Monitoring
- Normalized RU Consumption Monitoring Quiz5q
- Server-Side Latency Metrics
- Server-Side Latency Metrics Quiz5q
- Data Replication Monitoring
- Data Replication Monitoring Quiz5q
- Azure Monitor Alerts Configuration
- Azure Monitor Alerts Configuration Quiz5q
- Resource Logs Implementation
- Resource Logs Implementation Quiz5q
- Partition Throughput Monitoring
- Partition Throughput Monitoring Quiz5q
- Encryption Key Management
- Encryption Key Management Quiz5q
- Network-Level Access Control
- Network-Level Access Control Quiz5q
- Data Encryption Configuration
- Data Encryption Configuration Quiz5q
- Azure RBAC for Control Plane
- Azure RBAC for Control Plane Quiz5q
- Microsoft Entra ID for Data Plane
- Microsoft Entra ID for Data Plane Quiz5q
- CORS Settings Configuration
- CORS Settings Configuration Quiz5q
- Customer-Managed Keys
- Customer-Managed Keys Quiz5q
- Always Encrypted Implementation
- Always Encrypted Implementation Quiz5q
- Data Movement Strategy Selection
- Data Movement Strategy Selection Quiz5q
- SDK Bulk Operations for Data Movement
- SDK Bulk Operations for Data Movement Quiz5q
- Azure Data Factory Pipelines
- Azure Data Factory Pipelines Quiz5q
- Kafka Connector Integration
- Kafka Connector Integration Quiz5q
- Azure Stream Analytics Integration
- Azure Stream Analytics Integration Quiz5q
- Cosmos DB Spark Connector
- Cosmos DB Spark Connector Quiz5q
Enjoying the courses?
Everything stays free. Pro shows fewer ads, doubles the points you earn on every lesson and quiz so you progress twice as fast, unlocks half of every practice exam — plus full case studies — with the Learn & Exam study modes, and lets you read each lesson on one page.
- ✓ Fewer advertisements
- ✓ 2× points per lesson & quiz
- ✓ 50% of every exam unlocked
- ✓ Learn & Exam modes
- ✓ Distraction-free lessons