Garnet是微软开源的远程缓存存储系统,基于.NET 8构建,支持Redis协议,在读写性能、内存利用率上相比传统缓存方案有显著提升,非常适合.NET生态下的高性能缓存场景。它支持字符串、哈希、列表等多种数据结构,同时具备持久化、集群部署等能力,能够满足大部分业务缓存需求。

环境准备
首先需要在本地或服务器部署Garnet服务,也可以直接使用Docker快速启动实例,执行以下命令即可拉取并运行Garnet容器:
# 拉取Garnet镜像 docker pull ghcr.io/microsoft/garnet:latest # 启动Garnet服务,默认端口6379 docker run -d -p 6379:6379 --name garnet-server ghcr.io/microsoft/garnet:latest
然后在C#项目中安装对应的客户端依赖,目前官方推荐的.NET客户端是StackExchange.Redis,它完全兼容Garnet的Redis协议,无需额外适配:
# 通过NuGet安装客户端包 dotnet add package StackExchange.Redis
建立Garnet连接
在C#中连接Garnet服务和连接Redis的逻辑完全一致,首先创建连接配置,然后初始化连接实例:
using StackExchange.Redis;
public class GarnetConnectionFactory
{
private static readonly Lazy<ConnectionMultiplexer> LazyConnection = new Lazy<ConnectionMultiplexer>(() =>
{
// 配置Garnet服务地址,默认端口6379,无密码可直接连接
var configuration = ConfigurationOptions.Parse("localhost:6379");
// 可选配置:设置连接超时时间、重试次数等
configuration.ConnectTimeout = 5000;
configuration.RetryPolicy = new ExponentialRetry(5000, 10000);
return ConnectionMultiplexer.Connect(configuration);
});
public static ConnectionMultiplexer Connection => LazyConnection.Value;
public static IDatabase GetDatabase()
{
// 获取默认数据库实例,和Redis用法一致
return Connection.GetDatabase();
}
}
基础缓存操作示例
连接建立完成后,就可以进行常规的缓存增删改查操作,以下是常用的操作示例:
字符串类型缓存操作
public class StringCacheService
{
private readonly IDatabase _database;
public StringCacheService()
{
_database = GarnetConnectionFactory.GetDatabase();
}
// 设置缓存,过期时间30分钟
public async Task SetCacheAsync(string key, string value)
{
await _database.StringSetAsync(key, value, TimeSpan.FromMinutes(30));
}
// 获取缓存
public async Task<string> GetCacheAsync(string key)
{
var result = await _database.StringGetAsync(key);
return result.HasValue ? result.ToString() : null;
}
// 删除缓存
public async Task DeleteCacheAsync(string key)
{
await _database.KeyDeleteAsync(key);
}
}
哈希类型缓存操作
哈希类型适合存储对象类数据,避免整体序列化反序列化的性能损耗:
public class HashCacheService
{
private readonly IDatabase _database;
public HashCacheService()
{
_database = GarnetConnectionFactory.GetDatabase();
}
// 设置哈希字段
public async Task SetHashFieldAsync(string key, string field, string value)
{
await _database.HashSetAsync(key, field, value);
}
// 获取哈希字段
public async Task<string> GetHashFieldAsync(string key, string field)
{
var result = await _database.HashGetAsync(key, field);
return result.HasValue ? result.ToString() : null;
}
// 获取整个哈希对象
public async Task<Dictionary<string, string>> GetAllHashAsync(string key)
{
var entries = await _database.HashGetAllAsync(key);
return entries.ToDictionary(entry => entry.Name.ToString(), entry => entry.Value.ToString());
}
}
实际业务场景应用
以用户信息缓存为例,演示完整的缓存使用流程,优先从缓存读取,缓存不存在再查询数据库并回写缓存:
public class UserService
{
private readonly StringCacheService _cacheService;
// 假设的用户数据仓储
private readonly UserRepository _userRepository;
public UserService(StringCacheService cacheService, UserRepository userRepository)
{
_cacheService = cacheService;
_userRepository = userRepository;
}
public async Task<User> GetUserAsync(int userId)
{
string cacheKey = $"user:{userId}";
// 先从Garnet缓存读取
var cachedUserJson = await _cacheService.GetCacheAsync(cacheKey);
if (!string.IsNullOrEmpty(cachedUserJson))
{
return JsonSerializer.Deserialize<User>(cachedUserJson);
}
// 缓存未命中,查询数据库
var user = await _userRepository.GetByIdAsync(userId);
if (user != null)
{
// 回写缓存,设置30分钟过期
await _cacheService.SetCacheAsync(cacheKey, JsonSerializer.Serialize(user));
}
return user;
}
}
性能优化建议
- 尽量使用批量操作接口,减少网络往返次数,比如使用
StringSetAsync的批量重载方法一次性写入多个缓存 - 合理设置缓存过期时间,避免无效缓存占用过多内存,同时防止缓存雪崩
- 对于热点数据,可以考虑使用本地缓存+Garnet二级缓存的方案,进一步降低延迟
- 生产环境建议部署Garnet集群,提升可用性和整体吞吐量