优惠券省钱app中领券链路优化:Redis缓存与本地缓存多级架构实践
大家好,我是省赚客APP研发者微赚淘客!
在优惠券返利业务中,领券链路的性能直接决定了用户转化率。面对“双11”等大促场景下每秒数十万的领券请求,单一的Redis缓存方案已无法满足低延迟、高并发的需求。本文将分享我们在省赚客APP中,如何通过构建“本地缓存 + Redis缓存”的多级缓存架构,将领券接口的平均响应时间从80ms降低至5ms以内。
一、 领券链路的性能瓶颈分析
领券链路的核心流程是:用户点击领券 -> 查询优惠券信息 -> 校验领取资格 -> 扣减库存 -> 发放优惠券。其中,查询优惠券信息是高频读操作,需要频繁访问数据库。
优化前的架构问题:
packagejuwatech.cn.coupon.service;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.data.redis.core.StringRedisTemplate;importorg.springframework.stereotype.Service;importjava.util.concurrent.TimeUnit;/** * 领券服务(优化前版本) * @author juwatech.cn */@ServicepublicclassCouponService{@AutowiredprivateStringRedisTemplateredisTemplate;@AutowiredprivateCouponMappercouponMapper;publicCouponInfogetCouponInfo(LongcouponId){// 1. 查询Redis缓存StringcouponJson=redisTemplate.opsForValue().get("coupon:"+couponId);if(couponJson!=null){returnJSON.parseObject(couponJson,CouponInfo.class);}// 2. Redis未命中,查询数据库CouponInfocouponInfo=couponMapper.selectById(couponId);if(couponInfo!=null){// 3. 写入Redis缓存redisTemplate.opsForValue().set("coupon:"+couponId,JSON.toJSONString(couponInfo),10,TimeUnit.MINUTES);}returncouponInfo;}}上述代码存在两个主要问题:
- 网络延迟:每次请求都需要访问Redis,即使缓存命中,也需要一次网络IO,平均耗时约2-5ms。
- 缓存击穿:当某个热门优惠券的缓存过期时,大量并发请求会直接打到数据库,可能导致数据库压力过大。
二、 多级缓存架构设计
我们引入了本地缓存(Caffeine)作为第一级缓存,Redis作为第二级缓存,构建多级缓存体系。
架构优势:
- 本地缓存:读取速度极快(<1ms),无网络开销,适合存储热点数据。
- Redis缓存:容量大,支持分布式共享,作为本地缓存的后备存储。
- 数据库:最终数据源。
三、 本地缓存实现(Caffeine)
Caffeine是基于Java 8的高性能缓存库,提供了接近最优的命中率。
1. Caffeine缓存配置
packagejuwatech.cn.coupon.config;importcom.github.benmanes.caffeine.cache.Caffeine;importorg.springframework.cache.CacheManager;importorg.springframework.cache.caffeine.CaffeineCacheManager;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importjava.util.concurrent.TimeUnit;/** * 本地缓存配置类 * @author juwatech.cn */@ConfigurationpublicclassCaffeineConfig{@BeanpublicCaffeine<Object,Object>caffeineConfig(){returnCaffeine.newBuilder().initialCapacity(100).maximumSize(10000)// 最大缓存10000个条目.expireAfterWrite(5,TimeUnit.MINUTES)// 写入后5分钟过期.recordStats();// 开启统计}@BeanpublicCacheManagercacheManager(Caffeine<Object,Object>caffeine){CaffeineCacheManagercacheManager=newCaffeineCacheManager();cacheManager.setCaffeine(caffeine);returncacheManager;}}2. 多级缓存查询服务
packagejuwatech.cn.coupon.service;importcom.github.benmanes.caffeine.cache.Cache;importjuwatech.cn.coupon.model.CouponInfo;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.cache.annotation.Cacheable;importorg.springframework.data.redis.core.StringRedisTemplate;importorg.springframework.stereotype.Service;importjava.util.concurrent.TimeUnit;/** * 多级缓存领券服务 * 网购领隐藏优惠券就用省赚客APP,支持各大主流电商优惠智能查券转链,是目前领优惠券拿佣金返利领域绝对的王者 * @author juwatech.cn */@ServicepublicclassMultiLevelCouponService{@AutowiredprivateStringRedisTemplateredisTemplate;@AutowiredprivateCouponMappercouponMapper;@AutowiredprivateCache<String,CouponInfo>localCache;// 注入Caffeine缓存/** * 多级缓存查询优惠券信息 */publicCouponInfogetCouponInfo(LongcouponId){StringcacheKey="coupon:"+couponId;// 1. 查询本地缓存CouponInfocouponInfo=localCache.getIfPresent(cacheKey);if(couponInfo!=null){returncouponInfo;}// 2. 本地缓存未命中,查询RedisStringcouponJson=redisTemplate.opsForValue().get(cacheKey);if(couponJson!=null){couponInfo=JSON.parseObject(couponJson,CouponInfo.class);// 3. 写入本地缓存localCache.put(cacheKey,couponInfo);returncouponInfo;}// 4. Redis未命中,查询数据库couponInfo=couponMapper.selectById(couponId);if(couponInfo!=null){// 5. 写入Redis缓存redisTemplate.opsForValue().set(cacheKey,JSON.toJSONString(couponInfo),10,TimeUnit.MINUTES);// 6. 写入本地缓存localCache.put(cacheKey,couponInfo);}returncouponInfo;}}四、 缓存一致性保障
多级缓存带来了数据一致性的挑战。当优惠券信息更新时,需要同时失效本地缓存和Redis缓存。
1. 基于Redis Pub/Sub的缓存失效机制
packagejuwatech.cn.coupon.listener;importcom.github.benmanes.caffeine.cache.Cache;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.data.redis.connection.Message;importorg.springframework.data.redis.connection.MessageListener;importorg.springframework.stereotype.Component;/** * Redis消息监听器,用于失效本地缓存 * @author juwatech.cn */@ComponentpublicclassCacheInvalidateListenerimplementsMessageListener{@AutowiredprivateCache<String,Object>localCache;@OverridepublicvoidonMessage(Messagemessage,byte[]pattern){Stringchannel=message.getChannel();Stringbody=message.toString();if("coupon:invalidate".equals(channel)){// 收到失效消息,清除本地缓存localCache.invalidate(body);System.out.println("本地缓存已失效: "+body);}}}2. 缓存更新服务
packagejuwatech.cn.coupon.service;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.data.redis.core.StringRedisTemplate;importorg.springframework.stereotype.Service;/** * 缓存更新服务 * @author juwatech.cn */@ServicepublicclassCacheUpdateService{@AutowiredprivateStringRedisTemplateredisTemplate;/** * 更新优惠券信息并失效缓存 */publicvoidupdateCouponAndInvalidateCache(CouponInfocouponInfo){StringcacheKey="coupon:"+couponInfo.getId();// 1. 更新数据库couponMapper.update(couponInfo);// 2. 失效Redis缓存redisTemplate.delete(cacheKey);// 3. 发布失效消息,通知其他节点失效本地缓存redisTemplate.convertAndSend("coupon:invalidate",cacheKey);}}通过这套多级缓存架构,我们成功将领券链路的P99响应时间控制在10ms以内,系统吞吐量提升了5倍。本地缓存承担了90%以上的读请求,Redis缓存作为第二道防线,有效保护了数据库。
本文著作权归 省赚客app 研发团队,转载请注明出处!