As applications scale, repeatedly querying the database for the same information becomes increasingly expensive. Product catalogs, user profiles, application settings, and frequently accessed reference data often change far less frequently than they are requested. Without an effective caching strategy, these repeated queries increase database load, consume application resources, and slow response times.
While in-memory caching works well for a single application instance, it becomes ineffective in distributed environments where multiple servers or containers process requests independently. Distributed caching solves this problem by providing a shared cache that every application instance can access.
ASP.NET Core integrates seamlessly with Redis, making it one of the most popular distributed caching solutions for modern cloud-native applications.
In this article, you'll learn how to implement distributed caching using Redis, choose appropriate caching strategies, handle cache invalidation, and build production-ready caching solutions.
Why Distributed Caching Matters
The Limitations of In-Memory Caching
Consider an application deployed across multiple servers.
Load Balancer
│
┌─────────────┴─────────────┐
▼ ▼
ASP.NET Core Instance 1 ASP.NET Core Instance 2
│ │
Memory Cache A Memory Cache B
│ │
└─────────────┬─────────────┘
▼
SQL Database
Each application instance maintains its own cache.
Problems include:
Duplicate cached data
Higher memory usage
Inconsistent cache contents
Frequent database queries
Difficult cache invalidation
A shared distributed cache eliminates these issues.
What Is Redis?
Redis is an in-memory data store commonly used for:
Distributed caching
Session storage
Distributed locks
Message brokering
Leaderboards
Rate limiting
Because Redis stores data in memory, read operations are extremely fast compared to repeatedly querying a relational database.
Configuring Redis
Install the Redis caching package.
dotnet add package Microsoft.Extensions.Caching.StackExchangeRedis
Register Redis.
builder.Services.AddStackExchangeRedisCache(options =>
{
options.Configuration =
builder.Configuration.GetConnectionString("Redis");
options.InstanceName = "StoreApi:";
});
Why This Configuration?
IDistributedCache abstracts the underlying cache provider.
Using the Redis implementation allows your application to share cached data across every application instance without changing business logic.
Using the Distributed Cache
Inject IDistributedCache.
public sealed class ProductService
{
private readonly IDistributedCache _cache;
public ProductService(IDistributedCache cache)
{
_cache = cache;
}
}
Retrieve cached data.
var cachedProduct =
await _cache.GetStringAsync($"product:{id}");
if (cachedProduct is not null)
{
return cachedProduct;
}
Why Read the Cache First?
Reading from Redis is significantly faster than executing many database queries.
This reduces database load and improves response times for frequently requested data.
Storing Cached Data
After retrieving data from the database:
await _cache.SetStringAsync(
$"product:{id}",
json,
new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow =
TimeSpan.FromMinutes(15)
});
Why Configure Expiration?
Cached data eventually becomes stale.
Expiration ensures outdated information is removed automatically, reducing the likelihood of serving obsolete data.
Cache-Aside Pattern
The most common caching strategy is Cache-Aside.
Workflow:
Request
│
▼
Redis Cache
│
┌─┴───────────────┐
│ │
Hit Miss
│ │
▼ ▼
Response SQL Database
│
▼
Store in Redis
│
▼
Response
This pattern keeps the cache synchronized naturally while avoiding unnecessary writes.
Cache Invalidation
Caching is easy.
Keeping cached data accurate is the real challenge.
Example:
await _cache.RemoveAsync($"product:{id}");
After updating a product:
Save changes to the database.
Remove the cached entry.
Allow the next request to rebuild the cache.
This avoids serving outdated information.
End-to-End Implementation
Consider an online retail platform.
Architecture:
Join the conversation! Your thoughts help the community grow.