redis 缓存失效 备用方案 redis缓存失效时间设置_redis

背景:

这是血淋淋的真实的生产问题,问题过于复杂,我长话短说:

我们的APP推出了一项领取优惠券的活动,且限制每天最多可以领取1W张,就酱紫的功能。考虑到效率和线程安全的问题,使用Redis的计数器功能,利用计数器的原子性进行操作,并设置了每天0点自动失效。


问题:

当活动上线后没几天,就发生了“优惠券无法领取”的问题。

具体情况就是:当第一天券领完以后,存储计数器的key(下面用【numKey 】表示)在第二天0点(第一天的24点)并没有失效,导致第二天查询时,由于返回剩余数量仍然是 numKey = 0,导致无法继续领取


原因分析:

经过对日志和监控数据的分析,显示领券接口在0点前后的5分钟时间受到了恶意攻击,导致短时间内并发量成千倍激增,QPS均值维持在2600+水平。

我的猜想是:

短时间的高并发,会让保存优惠券数量的 numKey 一直处于活跃状态。由于我们的代码使用的是惰性删除策略(使用 expire 设置超时时间),导致在 0 点时Redis并没有立刻删除 numKey 主键,从而导致了后面一系列的问题,这是 Redis 的缓存失效策略决定的。

我们最终只是把问题定位在了Redis缓存失效策略上,更细节的原因.... 因为只是猜想,所以不能写出来误导别人。

被攻击只是问题的导火索,根本原因还是要从代码里找。

太啰嗦了,所以不写细节,都在下面的解决方案里了。


解决方案:

  1. 对并发敏感的接口,一定要做限流操作,可以用Redis的setnx实现,即:同一个用户(token)短期内不能多次访问;
  2. 在Redis需要自主更新的时间节点,最好限制下不要再有请求进来,业务上也不会在乎那几秒钟时间,如:代码里限制下在23点59分50秒后不再处理领券业务,用来保证0点后新的key能更新进来;
  3. 如果是这种很严谨的业务场景,Redis的删除不要过度依赖(expire),一定要主动删除(del),如:判断下新一天的0点啦,然后直接 del 旧的 key,然后创建新 key;
  4. 一天24小时都能领券这样的业务明显也不太合理,设置下领券时间,如:每天10:00~23:00.