应使用最新版 stackexchange.redis(2.7.x+),通过 connectionmultiplexer 单例复用连接,显式配置 abortconnect=false 等参数,对象存取需手动序列化,并发更新推荐 lua 脚本,docker 中注意 dns 和网络配置。

直接用 StackExchange.Redis,别碰过时的 ServiceStack.Redis 或原生 StackExchange.Redis 低版本(如 1.x),否则会踩连接泄漏、异步死锁、序列化兼容性等坑。
安装和基础连接怎么配
用 NuGet 安装最新稳定版 StackExchange.Redis(目前是 2.7.x+),不是 StackExchange.Redis.StrongName(已废弃)。
连接字符串建议显式指定关键参数,避免默认行为引发超时或重连风暴:
-
abortConnect=false:启动时不因初始连不上就抛异常 -
connectTimeout=5000:设为 5 秒,太短易失败,太长拖慢服务启动 -
syncTimeout=3000:同步操作上限,防止阻塞线程池 -
allowAdmin=true:仅开发/调试需要执行FLUSHDB等命令时开启
推荐用 ConnectionMultiplexer 单例复用,不要每次 new —— 它本身线程安全且管理连接池:
private static readonly Lazy<connectionmultiplexer> LazyConnection =
new Lazy<connectionmultiplexer>(() => ConnectionMultiplexer.Connect("localhost:6379,abortConnect=false,connectTimeout=5000"));</connectionmultiplexer></connectionmultiplexer>
存取对象为什么报错“Unable to serialize”
StackExchange.Redis 默认只接受 byte[],直接传 string 或自定义类会触发序列化失败,常见错误信息:System.NotSupportedException: The collection type 'System.Collections.Generic.List`1' is not supported.
必须自己处理序列化,推荐用 System.Text.Json(.NET Core 3.0+ / .NET 5+ 默认):
- 存对象:
db.StringSet("user:1001", JsonSerializer.Serialize(user)) - 取对象:
var json = db.StringGet("user:1001"); var user = JsonSerializer.Deserialize<user>(json!)</user>
注意:不要用 JsonConvert.SerializeObject(Newtonsoft.Json),除非项目强依赖它;若用,需确保所有属性可 public 读写,且无循环引用。
如何安全地做并发更新(比如库存扣减)
别用“先 GET 再 SET”这种两步操作——竞态条件必然发生。Redis 原生命令 DECR、INCR、GETSET 是原子的,但 C# 封装里得用 IServer 或 Lua 脚本。
推荐方案是 Lua 脚本,保证多条命令原子执行:
var script = @"if redis.call('GET', KEYS[1]) >= ARGV[1] then
return redis.call('DECRBY', KEYS[1], ARGV[1])
else
return -1
end";
var result = db.ScriptEvaluate(script, new RedisKey[] { "stock:item101" }, new RedisValue[] { "5" });
返回 -1 表示库存不足,其他值是扣减后剩余量。脚本里不能用 KEYS[0] 这种非法索引,下标从 1 开始。
为什么本地跑得好,上 Linux 容器就超时
典型表现:.NET 6+ 应用在 Docker 中连接 Redis 报 SocketFailure on GET 或 Timeout performing GET,但 redis-cli -h redis 可通。
原因通常是 DNS 解析 + 连接复用冲突:
- Docker 默认用
bridge网络,localhost指向容器自身,不是宿主机 - 若 Redis 在宿主机,应改用
host.docker.internal(Docker Desktop)或宿主机真实 IP(Linux Docker 需配置--add-host) - 连接字符串中去掉
ssl=true(除非 Redis 真开了 TLS)
验证方法:进容器执行 ping redis(服务名)或 telnet redis 6379,不通就不是代码问题。
最常被忽略的是连接生命周期管理——ConnectionMultiplexer 不要 Dispose 掉,除非应用彻底退出;如果用了 DI 容器(如 Microsoft.Extensions.DependencyInjection),注册为 Singleton 即可,别手动调 .Close() 或 .Dispose()。











