Introduction
As applications grow, database queries often become one of the biggest performance bottlenecks. Even well-optimized databases can struggle when thousands of users repeatedly request the same data. This is where caching becomes essential.
Distributed caching allows applications to store frequently accessed data in memory, reducing database load and improving response times. Among the various caching solutions available, Redis has become one of the most popular choices due to its speed, scalability, and reliability.
When combined with ASP.NET Core, Redis enables developers to build high-performance applications capable of handling large volumes of traffic while maintaining low latency.
In this article, you'll learn how distributed caching works, how to integrate Redis with ASP.NET Core, and the best practices for building scalable and efficient caching solutions.
What Is Distributed Caching?
A distributed cache stores data outside the application process, making it accessible across multiple application instances.
Unlike in-memory caching, where data exists only within a single application server, distributed caching allows all servers in a load-balanced environment to access the same cached data.
In-Memory Cache
Server 1
└── Cache A
Server 2
└── Cache B
Each server maintains its own cache.
Distributed Cache
Server 1 ──┐
Server 2 ──┼── Redis Cache
Server 3 ──┘
All servers share the same cache.
This architecture ensures consistency and improves scalability.
Why Choose Redis?
Redis is an open-source, in-memory data store designed for high-speed data access.
Key advantages include:
Redis can handle millions of operations per second, making it ideal for modern web applications and APIs.
Common Use Cases for Redis
Redis is frequently used for:
For example, product catalogs, user profiles, and dashboard statistics are excellent candidates for caching.
Setting Up Redis in ASP.NET Core
Install the Redis Package
Add the Redis caching package to your project.
dotnet add package Microsoft.Extensions.Caching.StackExchangeRedis
This package provides integration between ASP.NET Core and Redis.
Configure Redis
In Program.cs:
builder.Services.AddStackExchangeRedisCache(options =>
{
options.Configuration = "localhost:6379";
options.InstanceName = "MyApp";
});
The application can now communicate with the Redis server.
Using IDistributedCache
ASP.NET Core provides the IDistributedCache interface for interacting with distributed cache providers.
Inject the service:
public class ProductService
{
private readonly IDistributedCache _cache;
public ProductService(IDistributedCache cache)
{
_cache = cache;
}
}
This abstraction allows your code to remain independent of the underlying cache implementation.
Storing Data in Redis
Let's cache product information.
await _cache.SetStringAsync(
"product:1",
"Laptop",
new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow =
TimeSpan.FromMinutes(30)
});
The value is stored in Redis and automatically expires after 30 minutes.
Retrieving Cached Data
To retrieve data:
var productName =
await _cache.GetStringAsync("product:1");
if (productName != null)
{
Console.WriteLine(productName);
}
The application first checks Redis before accessing the database.
Implementing Cache-Aside Pattern
The Cache-Aside pattern is the most commonly used caching strategy.
The workflow is:
Check cache.
If data exists, return it.
If data does not exist, query the database.
Store the result in cache.
Return the data.
Example
public async Task<Product?> GetProductAsync(int id)
{
string cacheKey = $"product:{id}";
var cachedData =
await _cache.GetStringAsync(cacheKey);
if (cachedData != null)
{
return JsonSerializer.Deserialize<Product>(
cachedData);
}
var product =
await _dbContext.Products.FindAsync(id);
if (product != null)
{
await _cache.SetStringAsync(
cacheKey,
JsonSerializer.Serialize(product),
new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow =
TimeSpan.FromMinutes(10)
});
}
return product;
}
This approach dramatically reduces database traffic for frequently accessed data.
Choosing the Right Expiration Strategy
Cache expiration is critical for maintaining data accuracy.
Absolute Expiration
The cache expires after a fixed period.
new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow =
TimeSpan.FromMinutes(15)
}
Useful for:
Product information
Reports
Reference data
Sliding Expiration
The expiration timer resets whenever the data is accessed.
new DistributedCacheEntryOptions
{
SlidingExpiration =
TimeSpan.FromMinutes(10)
}
Useful for:
Caching API Responses
High-traffic APIs benefit significantly from Redis caching.
Without caching:
Request → Database → Response
With caching:
Request → Redis → Response
This reduces:
Database load
Response time
Infrastructure costs
For read-heavy applications, caching can provide substantial performance improvements.
Cache Invalidation Strategies
One of the most challenging aspects of caching is keeping data current.
When data changes in the database, the corresponding cache entry should be removed or updated.
Removing Cached Data
await _cache.RemoveAsync("product:1");
After removal, the next request retrieves fresh data from the database.
Common invalidation approaches include:
Monitoring Redis Performance
Monitoring helps ensure the cache remains effective.
Important metrics include:
Cache hit rate
Cache miss rate
Memory usage
Network latency
Command execution time
A low cache hit rate often indicates inefficient caching logic or overly aggressive expiration policies.
Common Caching Mistakes
Caching Everything
Not all data benefits from caching.
Avoid caching:
Large Cache Objects
Storing large objects increases memory consumption and network overhead.
Cache only the required data.
Missing Expiration Policies
Data should never remain in cache indefinitely unless there is a clear business requirement.
Ignoring Cache Invalidation
Stale data can lead to incorrect application behavior and poor user experience.
Best Practices
When implementing Redis caching in ASP.NET Core:
Use the Cache-Aside pattern for most scenarios.
Cache frequently accessed, read-heavy data.
Set appropriate expiration policies.
Keep cached objects small.
Monitor cache hit rates regularly.
Use meaningful cache key naming conventions.
Remove stale entries when data changes.
Avoid caching sensitive information unnecessarily.
Benchmark application performance before and after introducing caching.
Use Redis as a complement to database optimization, not a replacement.
Conclusion
Redis is a powerful distributed caching solution that can significantly improve the performance, scalability, and responsiveness of ASP.NET Core applications. By reducing database traffic and serving frequently requested data directly from memory, Redis enables applications to handle higher workloads with lower latency.
Implementing caching effectively requires careful planning around expiration policies, invalidation strategies, and cache design. By following proven patterns such as Cache-Aside and adhering to caching best practices, developers can build high-performance systems that scale efficiently while delivering a better user experience.
For modern ASP.NET Core applications, Redis remains one of the most effective tools for achieving fast, reliable, and scalable performance.