RedissonClient注入失败导致nil panic,需用Provider函数封装并纳入Wire依赖注入;锁key须分段设计避免串行瓶颈;tryLock的waitTime和leaseTime需依压测数据精确设定。

直接用 RedissonClient 会 panic,因为没注入成功 —— 这是 Kratos 里分布式锁最常卡死的第一步。
RedissonClient 注入失败导致 lock 为 nil
在 Kratos 中写 redissonClient.GetLock(key).TryLock(...) 然后 panic 报 nil pointer dereference,不是锁逻辑错,是 redissonClient 根本就是 nil。
- Wire 不支持直接 new
RedissonClient,它必须由*redis.Options构建,且生命周期要和App对齐 - 必须封装成 Provider 函数,例如:
func NewRedissonClient(opts *redis.Options) *redisson.Client - 该函数要显式加入
wire.go的ProviderSet:比如wire.NewSet(NewRedissonClient) - Provider 必须放在
data/redis/下,并由data.NewData()初始化,确保早于service层构建
锁 key 设计不合理引发串行瓶颈
写成 "lock:product:" + productId 表面看没问题,实际把所有抢同一商品的请求全压到一把锁上,吞吐归零。
- key 必须带分段维度,推荐格式:
fmt.Sprintf("seckill:%s:prod:%d:seg:%d", activityID, productId, userID%segmentCount) -
segmentCount不能拍脑袋定:按预估峰值 QPS 和单段平均处理耗时反推(例:单段极限 200 QPS,峰值 1000 QPS → 至少 5 段) - key 中必须含
activityID或时间戳,避免不同批次活动复用旧锁,导致误删或冲突
tryLock 的 waitTime 和 leaseTime 配错导致超卖或饥饿
TryLock(waitTime, leaseTime, timeUnit) 两个时间参数设错,锁就形同虚设。
-
leaseTime(持有时间)设太短(如 3s),而业务实际耗时 4s,锁自动释放,其他协程趁虚而入 → 超卖 -
waitTime(最大等待时间)设太小(如 100ms),高并发下大量请求直接返回失败 → 用户看到“稍后再试” - 建议:
leaseTime ≥ 业务最大可能耗时 × 1.5;waitTime按接口 SLA 定(如 99% 请求应在 800ms 内完成,则设为 800) - 解锁前必须校验
lock.IsHeldByCurrentThread(),否则可能误删别人持有的锁
真正容易被忽略的是:锁 key 的分段逻辑和 leaseTime 的取值依据,这两项不靠压测反推、只靠经验拍板,上线后大概率在大促流量下暴露问题。没有银弹,只有对业务路径的精确测量和对锁生命周期的诚实估算。











