Database queries, external API calls, and expensive computations can quickly become performance bottlenecks as application traffic grows. Even well-optimized SQL queries introduce latency when executed repeatedly for the same data.
Redis is a high-performance, in-memory data store commonly used as a distributed cache. When integrated with ASP.NET Core, Redis can significantly reduce response times, lower database load, and improve application scalability. However, caching the wrong data—or using an inappropriate caching strategy—can create stale data, unnecessary complexity, and difficult-to-debug consistency issues.
Rather than caching everything, this article explains practical Redis caching strategies, when to use them, and how to implement them effectively in ASP.NET Core.
Note: Caching improves performance only when cache hits significantly outnumber cache misses. Always measure cache effectiveness using metrics such as hit ratio and latency.
Why Use Redis Caching?
Every request that reaches the database consumes CPU, memory, network bandwidth, and I/O resources.
Caching frequently accessed data reduces repeated database queries and improves response times.
Typical benefits include:
Not every piece of data should be cached. Frequently changing data may benefit more from direct database access.
What Should Be Cached?
Redis works best for data that is frequently read but changes infrequently.
Common candidates include:
Product catalogs
User profiles
Application configuration
Country and currency lists
Dashboard summaries
Frequently accessed reports
API responses
Session data
Avoid caching highly volatile or sensitive information unless there is a clear business requirement.
Caching Architecture
flowchart LR
A[Client]
B[ASP.NET Core API]
C{Cache Hit?}
D[(Redis)]
E[(SQL Server)]
A --> B
B --> C
C -->|Yes| D
C -->|No| E
E --> D
D --> B
B --> A
When cached data exists, Redis responds immediately. Otherwise, the application retrieves the data from the database, stores it in Redis, and returns the response.
Installing Redis
Install the Redis caching package.
dotnet add package Microsoft.Extensions.Caching.StackExchangeRedis
This package integrates Redis with the ASP.NET Core dependency injection system.
Configuring Redis
Register Redis during application startup.
builder.Services.AddStackExchangeRedisCache(options =>
{
options.Configuration =
"localhost:6379";
options.InstanceName = "MyApp:";
});
In production, replace the local connection string with your managed Redis endpoint.
Reading from the Cache
Inject IDistributedCache into your service.
public class ProductService
{
private readonly IDistributedCache _cache;
public ProductService(
IDistributedCache cache)
{
_cache = cache;
}
}
IDistributedCache provides a consistent API regardless of the underlying cache provider.
Cache-Aside Pattern
The Cache-Aside pattern is the most common Redis strategy.
var cacheKey = $"product:{id}";
var cached =
await _cache.GetStringAsync(cacheKey);
if (cached != null)
{
return JsonSerializer.Deserialize<Product>(cached);
}
var product =
await repository.GetByIdAsync(id);
await _cache.SetStringAsync(
cacheKey,
JsonSerializer.Serialize(product),
new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow =
TimeSpan.FromMinutes(10)
});
return product;
The application first checks Redis. If the data isn't found, it retrieves it from the database and stores it in the cache for future requests.
Setting Expiration Policies
Expiration prevents stale data from remaining in the cache indefinitely.
var options =
new DistributedCacheEntryOptions
{
SlidingExpiration =
TimeSpan.FromMinutes(15),
AbsoluteExpirationRelativeToNow =
TimeSpan.FromHours(1)
};
Combining sliding and absolute expiration provides a balance between freshness and performance.
Cache Invalidation
Whenever cached data changes, invalidate the associated cache entry.
await repository.UpdateAsync(product);
await _cache.RemoveAsync(
$"product:{product.Id}");
Failing to invalidate outdated entries is one of the most common causes of stale data.
Choosing an Expiration Strategy
Different data requires different expiration periods.
| Data Type | Suggested Expiration |
|---|
| Product Catalog | 30–60 minutes |
| User Profile | 10–30 minutes |
| Dashboard Data | 5–15 minutes |
| Configuration | Several hours |
| Reference Data | 24 hours or longer |
Choose expiration values based on how frequently the underlying data changes.
Common Redis Data Structures
Redis supports multiple data types.
| Data Structure | Common Use Case |
|---|
| String | Cached objects |
| Hash | User profiles |
| List | Activity feeds |
| Set | Unique values |
| Sorted Set | Leaderboards |
| Stream | Event processing |
Selecting the appropriate data structure can improve both performance and memory efficiency.
Monitoring Cache Performance
Monitor these metrics regularly:
Cache hit ratio
Cache miss ratio
Average response time
Memory utilization
Key expiration rate
Network latency
Eviction count
A low cache hit ratio often indicates ineffective caching or overly aggressive expiration settings.
Common Production Mistakes
| Problem | Root Cause |
|---|
| Stale data | Cache not invalidated after updates |
| Low cache hit ratio | Expiration time too short |
| Excessive memory usage | Large objects cached unnecessarily |
| Cache stampede | Multiple requests rebuilding the same cache entry |
| Serialization errors | Incompatible object versions |
| Performance degradation | Caching frequently changing data |
Many caching issues originate from poor cache design rather than Redis itself.
Best Practices
Cache only frequently accessed data.
Use meaningful cache keys.
Configure appropriate expiration policies.
Invalidate cache entries after updates.
Monitor cache hit ratios continuously.
Keep cached objects reasonably small.
Protect against cache stampedes for expensive operations.
Common Anti-Patterns
Avoid these common mistakes:
Caching every database query.
Using extremely long expiration times for changing data.
Ignoring cache invalidation.
Storing very large objects in Redis.
Using identical expiration values for every cache entry.
Assuming cached data is always current.
FAQ
Should every ASP.NET Core application use Redis?
Not necessarily. Applications with low traffic or minimal repeated reads may not benefit enough to justify the additional infrastructure.
What is the Cache-Aside pattern?
Cache-Aside is a strategy where the application first checks the cache. If the requested data isn't found, it retrieves the data from the database, stores it in the cache, and returns the result.
Is Redis a database replacement?
No. Redis is typically used as a caching layer in front of a primary database rather than as a replacement for transactional data storage.
How long should cached data live?
It depends on how frequently the data changes. Frequently updated information should have shorter expiration times, while relatively static reference data can remain cached much longer.
Conclusion
Redis is one of the most effective ways to improve ASP.NET Core application performance when used appropriately. By reducing repetitive database queries and serving frequently accessed data directly from memory, Redis helps lower latency, reduce infrastructure load, and improve scalability.
Successful caching is not about storing as much data as possible. It requires selecting the right data, choosing appropriate expiration policies, implementing reliable cache invalidation, and continuously monitoring cache performance. When applied thoughtfully, Redis becomes a valuable component of a fast, scalable, and production-ready ASP.NET Core architecture.