Caching is a common technique aimed at improving the performance and scalability of a system. To cache data, frequently accessed data is temporarily copied into fast storage located close to the application. If this fast data store is closer to the application than the original source, caching can dramatically improve the response time of client applications by serving data more quickly.

Cache Types
Private In-Memory Cache
A private cache means the caching service runs on the same server as the application; it is part of the application, usually a library or module. A private cache is typically an in-memory cache, such as MemoryCache or ConcurrentDictionary. It is a lightweight cache suitable for small applications. The advantage of a private cache is that it is simple and easy to use; the drawback is that data cannot be shared across servers, making it unsuitable for large applications. The IMemoryCache interface can be used to work with a private cache in .NET.

services.AddMemoryCache();
public class MyService(IMemoryCache cache) : IService
{
public async Task Test()
{
cache.Set("key", "value");
var value = cache.Get("key");
}
}
Distributed Shared Cache
A distributed cache means the caching service runs across multiple servers; it is an independent service that can share data across servers. A distributed cache is typically an in-memory cache such as Redis, Memcached, or Garnet. It is a high-performance, low-latency cache suitable for large applications. The advantages of a distributed cache are high performance and low latency, with support for large-scale data storage and access; the drawbacks are higher complexity and the need for additional server resources. The IDistributedCache interface can be used to work with a distributed cache in .NET.

services.AddMemoryDistributedCache();
public class MyService(IDistributedCache cache) : IService
{
public async Task Test()
{
await cache.SetStringAsync("key", "value");
var value = await cache.GetStringAsync("key");
}
}
Using the Redis Distributed Cache
Redis is a high-performance, open source distributed cache system. It is an in-memory database that supports a variety of data structures, such as strings, lists, hash tables, and sets. Redis is designed to provide a high-performance, low-latency distributed cache system that supports large-scale data storage and access while offering high availability and scalability. Redis uses the RESP protocol and is written in C, with support for many languages such as C, C++, C#, Java, Python, and Node.js. In .NET, you can use the open source StackExchange.Redis library to work with the cache.
Redis for Windows is no longer maintained. You can install Redis using WSL2 or Docker on Windows. Memurai can also be used as a Redis alternative; Memurai is a Redis service for Windows that supports most Redis features and lets you run a Redis service on Windows.
Containerized Redis Service
docker run --name my-redis -d -p 6379:6379 -e REDIS_PASSWORD=guest redis
Testing Client Tools
Use redis-cli to connect to the Linux cache server and run the following commands for testing.
redis-cli -h localhost -p 6379 -a Password
auth Password
set key value
get key
Using the StackExchange.Redis Client
donet add package StackExchange.Redis
using StackExchange.Redis;
var redis = ConnectionMultiplexer.Connect("localhost:6379,password=guest");
var db = redis.GetDatabase();
db.StringSet("key", "value");
var value = db.StringGet("key");
Using the IDistributedCache Interface
dotnet add package Microsoft.Extensions.Caching.StackExchangeRedis
services.AddStackExchangeRedisCache(options =>
{
options.Configuration = "localhost:6379,password=guest";
options.InstanceName = "SampleInstance";
});
using Microsoft.Extensions.Caching.Distributed;
public class MyService(IDistributedCache cache) : IService
{
public async Task Test()
{
await cache.SetStringAsync("key", "value");
var value = await cache.GetStringAsync("key");
}
}
Using the IDistributedCache interface is recommended. It is an abstract interface that makes it easy to switch between different cache implementations such as Redis, Memcached, or Garnet, and it supports the ASP.NET Core caching middleware, making it convenient to use caching in ASP.NET Core.
Using Microsoft’s Garnet Distributed Cache
Garnet is an open source distributed cache system from Microsoft. It is a high-performance, low-latency distributed cache that supports a variety of data structures, such as strings, lists, hash tables, and sets. Garnet is designed to provide a high-performance, low-latency distributed cache system that supports large-scale data storage and access while offering high availability and scalability.
Garnet uses the popular RESP protocol and is written in C#. It supports most Redis commands while providing additional features such as cluster setup, data persistence, data replication, and data sharding. In C#, you can use the open source StackExchange.Redis library to work with the cache.
https://microsoft.github.io/garnet/docs
Self-Hosting in a .NET Program
dotnet add package Microsoft.Garnet
using Garnet;
try
{
using var server = new GarnetServer(args);
server.Start();
Thread.Sleep(Timeout.Infinite);
}
catch (Exception ex)
{
Console.WriteLine($"Unable to initialize server due to exception: {ex.Message}");
}
Running in a Docker Container
docker run --name garnet -d -p 6379:6379 --ulimit memlock=-1 ghcr.io/microsoft/garnet --auth Password --password guest
Running as a Windows Service
dotnet add package Microsoft.Garnet
dotnet add package Microsoft.Extensions.Hosting.WindowsServices
public class GarnetService(ILogger<GarnetService> logger) : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
if (!stoppingToken.IsCancellationRequested)
{
string[] commandLineArgs = Environment.GetCommandLineArgs();
using var server = new GarnetServer(commandLineArgs);
logger.LogInformation("Starting Garnet server...");
try
{
server.Start();
}
catch (Exception ex)
{
logger.LogError(ex, "An error occurred while starting the Garnet server.");
}
logger.LogInformation("Garnet server started.");
await Task.Delay(Timeout.Infinite, stoppingToken);
}
}
}
var builder = Host.CreateApplicationBuilder(args);
builder.Services.AddWindowsService(options =>
{
options.ServiceName = "Garnet Service";
});
builder.Services.AddHostedService<GarnetService>();
var host = builder.Build();
host.Run();
sc.exe create GarnetService binpath= "C:\Users\GarnetWindowsService.exe" start= auto
sc.exe start GarnetService
sc.exe stop GarnetService
sc.exe delete GarnetService
Using a Distributed Cache in the Basket Service
dotnet add package Microsoft.Extensions.Caching.StackExchangeRedis
builder.Services.AddStackExchangeRedisCache(options =>
{
options.Configuration = builder.Configuration.GetConnectionString("MyRedisConStr");
});
High-Availability Distributed Cache Clusters
Building a high-availability cluster with multiple cache servers can improve the availability and reliability of the system.
https://microsoft.github.io/garnet/docs/cluster/overview
https://redis.io/topics/cluster-tutorial
services.AddStackExchangeRedisCache(options =>
{
options.Configuration = "localhost:6379,localhost:6380,localhost:6381";
options.InstanceName = "SampleInstance";
});
services.AddStackExchangeRedisCache(options =>
{
options.InstanceName = "SampleInstance";
options.ConfigurationOptions = new ConfigurationOptions
{
Password = "Password",
AbortOnConnectFail = false,
EndPoints =
{
{ "localhost", 6379 },
{ "localhost", 6380 },
{ "localhost", 6381 }
}
};
});
Caching Best Practices
https://learn.microsoft.com/zh-cn/azure/architecture/best-practices/caching
https://learn.microsoft.com/zh-cn/azure/architecture/patterns/cache-aside
Caching Concepts
Cache avalanche, cache penetration, cache breakdown, cache warm-up, cache update, cache degradation, cache hit rate, sliding expiration, absolute expiration, no expiration, and cache dependencies.
https://developer.aliyun.com/article/841392
IDistributedCache expiration times fall into two kinds: absolute expiration and sliding expiration.
Distributed Cache Monitoring Tools
The RedisInsight tool can be used to monitor a Redis distributed cache system.
https://redis.io/docs/latest/operate/redisinsight/install
docker run -d --name redisinsight -p 5540:5540 redis/redisinsight:latest
Using the Aspire Component Library for Distributed Caching
Aspire Host Project Configuration
dotnet add package Aspire.Hosting.Redis
var redis = builder.AddRedis("cache");
builder.AddProject<Projects.ExampleProject>().WithReference(redis)
Microservice Project Configuration
dotnet add package Aspire.StackExchange.Redis.DistributedCaching
builder.AddRedisDistributedCache("cache");