Adjusting Database Indexes
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
Lesson: Mastering Indexing Strategies in Azure Cosmos DB
Introduction: Why Indexing Matters
When working with Azure Cosmos DB, you are interacting with a globally distributed, multi-model database service designed for high-scale applications. One of the most common reasons developers experience performance degradation or unexpected cost spikes in Cosmos DB is a misunderstanding of how the indexing engine works. Unlike traditional relational databases where you might manually define indexes on specific columns to speed up JOIN operations or WHERE clauses, Cosmos DB uses an automatic indexing policy by default. While this "index everything" approach is excellent for getting started and handling unknown query patterns, it can become a significant bottleneck as your data volume grows and your throughput requirements increase.
Indexing is the process of creating a secondary data structure that allows the database engine to locate specific records without scanning the entire collection. In Cosmos DB, every item inserted into a container is automatically indexed. By default, the indexing engine includes every property of your JSON documents. While this makes your queries fast out of the box, it consumes Request Units (RUs)—the currency of Cosmos DB—every time you perform a write operation. Each write requires the engine to update these indexes, which means the more indexes you have, the more expensive your writes become.
Understanding how to tune, restrict, or optimize these indexes is the difference between a high-performing, cost-effective application and one that suffers from high latency and bloated RU consumption. In this lesson, we will explore the mechanics of the Cosmos DB indexing engine, learn how to modify indexing policies, and examine strategies to balance read performance against write efficiency.
The Mechanics of Cosmos DB Indexing
To optimize your database, you must first understand how the index is structured. Cosmos DB uses a B-tree based index for range queries and a hash-based index for equality queries. When you save a JSON document, the engine decomposes the tree structure of your document into a flat representation and creates entries in the index for every path.
The Default Indexing Policy
By default, every container is created with an indexing policy that includes every path (/*) for every data type. It also includes support for range queries for both strings and numbers. This is a "greedy" policy. It ensures that any query you write against your document structure will be served by an index, preventing expensive full-container scans. However, this convenience comes with a cost: storage overhead and increased write latency.
Callout: The Trade-off of Automatic Indexing Automatic indexing is a double-edged sword. It provides immediate query performance for any property in your document, but it creates a "write tax." Every time you insert, replace, or delete a document, the index must be updated. For write-heavy workloads, this can significantly increase your RU usage compared to a more surgical indexing approach.
Indexing Paths
An indexing path is the sequence of keys that leads to a specific value in your JSON document. For example, in a document like {"user": {"name": "Alice"}}, the path is /user/name. You can define how the indexing engine treats these paths by specifying:
- Included Paths: Paths that you want the engine to track.
- Excluded Paths: Paths that you do not need to query, which should be ignored to save resources.
By explicitly defining these paths, you tell the engine exactly what it needs to focus on. If you have a massive document with nested metadata that you never query, you should exclude that path to reduce the index size and improve write performance.
Implementing Custom Indexing Policies
Modifying the indexing policy is done via the Azure Portal, the Azure CLI, or the SDKs. The policy is defined as a JSON document that resides within the container configuration. Let's look at how to construct and apply these policies.
Step-by-Step: Updating the Indexing Policy via Azure Portal
- Navigate to your Azure Cosmos DB account in the portal.
- Select the Data Explorer tab from the left-hand menu.
- Choose your database and then select the specific container you wish to optimize.
- Click on Settings in the top menu bar.
- Locate the Indexing Policy tab.
- Here, you will see the JSON representation of the current policy. You can edit this directly.
- Once you have made your changes, click Save.
Warning: Modifying an indexing policy is a potentially long-running operation. When you change the policy, Cosmos DB must rebuild the index based on the new rules. During this time, your container will continue to be available, but query performance may fluctuate, and the index rebuild will consume RUs. Always perform these changes during off-peak hours if possible.
Example: A Selective Indexing Policy
Suppose you have a document structure where you only ever query by email and createdDate. You do not need to index the biography or profilePictureUrl fields. Your custom policy would look like this:
{
"indexingMode": "consistent",
"automatic": true,
"includedPaths": [
{
"path": "/email/?"
},
{
"path": "/createdDate/?"
}
],
"excludedPaths": [
{
"path": "/*"
}
]
}
In this example, we have set the excludedPaths to the wildcard /*, which means "exclude everything." We then add back specific includedPaths for email and createdDate. The /? suffix indicates that the index should support range queries for those specific properties.
Indexing Modes: Consistent vs. Lazy
Cosmos DB offers two primary indexing modes that dictate how and when index updates occur:
- Consistent: This is the default. When you perform a write, the index is updated synchronously. Your queries will always return the most up-to-date results. This ensures strong consistency in your query results.
- None: This disables indexing entirely. You might choose this if you are using Cosmos DB as a simple key-value store where you only ever retrieve documents by their
idand partition key. This provides the highest possible write performance because there is no index overhead.
Note: Previously, Cosmos DB supported a "Lazy" indexing mode, but this has been deprecated in favor of Consistent indexing. Always default to Consistent unless you have a specific, high-scale write scenario that requires disabling indexing entirely.
Best Practices for Query Performance
Optimizing indexes is not just about excluding paths; it is about writing queries that the index can actually use. Even with a perfect index, a poorly written query can force a full collection scan.
1. Avoid Functions in the WHERE Clause
When you use a system function in your WHERE clause, the indexing engine often cannot use the index for that property. For example, if you have an index on /name and you write:
SELECT * FROM c WHERE UPPER(c.name) = 'ALICE'
The database engine must scan every document to apply the UPPER function before checking for equality. Instead, store the name in uppercase in your document and query against that field directly.
2. Use the Partition Key
The partition key is the most critical component of a Cosmos DB query. If your query includes the partition key in the filter, the engine can route the query directly to the relevant physical partition. If you omit the partition key, the query becomes a "cross-partition" query, which is significantly more expensive and slower. Always include the partition key in your WHERE clause whenever possible.
3. Favor Equality over Range Queries where Possible
Equality queries (using =) are generally faster and cheaper than range queries (using >, <, BETWEEN). If your business logic allows for equality lookups, structure your documents to support them. If you must use range queries, ensure that you have explicitly enabled range indexing for that specific path in your indexing policy.
4. Indexing for Sorting
If you frequently use ORDER BY in your queries, you must ensure that the property you are sorting by is included in the index. Furthermore, if you are sorting by multiple properties, you may need to implement a Composite Index.
Advanced Technique: Composite Indexes
A composite index is required when your query filters by multiple properties or sorts by multiple properties. While individual indexes are helpful, they are not sufficient for multi-property queries.
For example, consider this query:
SELECT * FROM c WHERE c.category = 'Electronics' ORDER BY c.price DESC
To optimize this, you need a composite index that covers both category and price. Without it, the engine has to retrieve all documents matching "Electronics," then sort them in memory, which is highly inefficient.
Defining a Composite Index
You add composite indexes in the compositeIndexes section of your indexing policy JSON:
"compositeIndexes": [
[
{
"path": "/category",
"order": "ascending"
},
{
"path": "/price",
"order": "descending"
}
]
]
This configuration tells the engine to pre-sort the data based on these two fields. Now, when your query runs, the database can retrieve the data already in the correct order, drastically reducing the RU cost and latency.
Common Pitfalls and How to Avoid Them
1. The "Everything is Indexed" Trap
Many developers leave the default policy in place for years. As the document size grows, the index becomes massive, consuming significant RU budget on every write.
- Fix: Audit your queries using the Query Metrics in the Azure Portal. If you see a query that is not using the index (or performing a full scan), add the necessary path. If you have paths that are never queried, remove them.
2. Oversized Indexing Policies
Adding too many composite indexes can also be problematic. While they make queries faster, they increase the storage cost and the complexity of the write path.
- Fix: Only create composite indexes for the most critical, high-frequency queries. Use the "Query Stats" feature in the Portal to identify which queries are consuming the most RUs and target those specifically.
3. Ignoring the Partition Key
This is the most frequent mistake. A query that ignores the partition key is a "fan-out" query. It must hit every physical shard in your cluster, collect the results, and aggregate them.
- Fix: Re-evaluate your container design. If your queries frequently need to aggregate data across partitions, you might be using the wrong partition key.
4. Forgetting to Re-index
When you change an indexing policy, the background index rebuild process starts. If you have a multi-terabyte collection, this can take a long time.
- Fix: Always check the status of your indexing progress. You can monitor this in the Azure Portal under the "Indexing Policy" tab. Do not assume that your new policy is fully active the second you click save.
Comparison: Indexing Strategies
| Strategy | Best For | Pros | Cons |
|---|---|---|---|
| Default Policy | Development/Prototyping | No configuration needed, works for all queries. | Expensive writes, high storage overhead. |
| Selective Indexing | Production with known query patterns | Efficient writes, lower storage costs. | Requires maintenance if queries change. |
| Composite Indexing | Complex queries with filters/sorts | Extremely fast reads for multi-field queries. | Increases write cost and complexity. |
| No Indexing | Key-Value lookups only | Fastest possible writes. | No query capability beyond ID. |
Practical Example: Optimizing a Shopping Cart
Imagine a shopping cart container. The documents look like this:
{
"id": "cart123",
"userId": "userA",
"items": [...],
"lastUpdated": "2023-10-01T10:00:00Z",
"status": "active"
}
If your application frequently runs a query like:
SELECT * FROM c WHERE c.userId = 'userA' AND c.status = 'active' ORDER BY c.lastUpdated DESC
The Optimization Steps:
- Partition Key: Ensure
userIdis the partition key. - Composite Index: Create a composite index for
(status, lastUpdated). - Selective Paths: Exclude the
itemsarray from the index if you never perform aWHEREclause check on the contents of the cart. This will significantly reduce index size if the carts are large.
By making these changes, you transform a cross-partition, expensive sort operation into a targeted, indexed, and pre-sorted retrieval.
Summary of Best Practices for Production
- Start with the default, then prune: It is safer to start with the default index and disable paths you don't need than to start with a blank slate and guess what you might need later.
- Monitor RU Consumption: Use the Azure Monitor metrics to track RU usage per request. If you see a sudden spike in RUs for a specific query, check if it is performing a full scan.
- Use the SDK for Testing: When testing new indexing policies, use the Cosmos DB SDK to run your queries and inspect the
RequestChargeproperty in the response headers. This gives you exact data on how your changes affect cost. - Documentation: Keep a record of why specific composite indexes exist. If a developer removes a "useless-looking" index later, they might inadvertently tank the performance of a critical report.
- Automation: Use Infrastructure as Code (IaC) tools like Bicep or Terraform to manage your indexing policies. This ensures your production environment remains consistent and reproducible.
Key Takeaways
- Indexing is a Trade-off: Every index you add makes reads faster but makes writes more expensive. Always balance these two needs based on your application's specific workload.
- Default Policy is Greedy: The default indexing policy covers everything. This is great for flexibility but usually inefficient for large-scale production workloads.
- Exclude Unnecessary Paths: If you never query a field, exclude it from the indexing policy. This reduces the index size and lowers the RU cost of write operations.
- Composite Indexes are Power Tools: Use them for queries that filter or sort by multiple fields. They are essential for performance but should be used sparingly to avoid excessive write overhead.
- Partition Key is King: No amount of indexing can compensate for a missing partition key. Always include the partition key in your queries to avoid cross-partition scans.
- Background Rebuilds: Be aware that changing an indexing policy triggers a background rebuild. Monitor the progress to ensure the new policy is fully applied before expecting performance gains.
- Audit Regularly: Indexing requirements change as your application evolves. Periodically review your query metrics to see if your indexing policy still aligns with your actual query patterns.
Frequently Asked Questions (FAQ)
Q: Can I index an array?
A: Yes, Cosmos DB allows you to index arrays. If you have an array of tags, you can index the individual elements to support queries like SELECT * FROM c WHERE 'Electronics' IN c.tags.
Q: What happens if I make a mistake in my indexing policy? A: If you exclude a path that you later need to query, your queries will simply stop working (they will return no results or require a full scan). You can always add the path back to the policy, and the index will rebuild.
Q: Do I need to index the id field?
A: The id field and the partition key are indexed by default and cannot be removed from the index. This ensures that lookups by ID and partition key are always fast.
Q: How do I know if my query is using an index? A: You can use the "Query Stats" in the Azure Portal. Look for "Index Utilization" metrics. If the index utilization is low or zero, your query is likely performing a full scan, and you need to investigate your indexing policy or query structure.
Q: Does indexing affect the cost of read operations? A: Yes, efficient indexing lowers the cost of read operations by reducing the number of documents the engine must scan. A well-indexed query will consume fewer RUs than a query that requires a full collection scan.
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