虽然面对的是高并发场景,单今天只关注缓存,不讨论线程安全和锁相关的问题。
前言
日常工作中,时常要面对抽奖活动,奖励发放,商品秒杀等大流量高并发的场景。
高并发场景面对的第一个问题是DB的IO瓶颈。 这时比较通用缓存方式是加一层redis,用内存的性能来解决IO的瓶颈。
但是引入Redis就一劳永逸了嘛?不是的,相对应的高并发场景又会引发Redis热KEY和大KEY的问题,造成带宽拉胯。
这时可以在做一层本地缓存。那么问题又来了,本地缓存没有过期时间啊,因此就可以用GUAVA CACHE来解决,本质上他就是封装了一个过期时间,至于过期策略,一般有三种,定时删除,定期删除,惰性删除,本地缓存,guava采用惰性删除的方式就很合理。感兴趣的同学,可以看下教程和源码http://ifeve.com/google-guava,这里不具体展开了。
附一张关键源码图:
场景与使用:
场景:动态配置获取, 交易链路,需要获取商家报名了活动,报名与否,会走不同的优惠策略。
首先要注意,为防止缓存击穿,我们不能依赖用户请求来获取配置,及时加锁也不行,在交易链路使用分布式锁阻断了交易就很坑。 所以,我们后台起个TASK将数据跑入redis,缓存时间设置了3天防止过期。
private List<Long> getSignupSellerUids(Long sellerActivityId) {
// 报名商家sellerUid 本地缓存
List<Long> localSignedupUids =
signupUidsLocalCache.getIfPresent(AllowanceConstant.ALLOWANCE_SIGNEDUP_SELLERS_CACHE_KEY);
if (Validator.isNotNull(localSignedupUids)) {
return localSignedupUids;
}
// 本地没命中 Redis缓存
String signedUpCacheKey = BeidianAllowanceCacheUtils.getSignedUpSellersCacheKey(sellerActivityId);
String confJsonStr = beidianRedisCacheClient.get(signedUpCacheKey);
List<Long> cacheSignedUids = Validator.isNotNullOrEmpty(JSON.parseArray(confJsonStr, Long.TYPE)) ?
JSON.parseArray(confJsonStr, Long.TYPE) :
Collections.emptyList();
if (!cacheSignedUids.isEmpty()) {
signupUidsLocalCache.put(AllowanceConstant.ALLOWANCE_SIGNEDUP_SELLERS_CACHE_KEY, cacheSignedUids);
return cacheSignedUids;
}
// 这里已经是异常状态了。
return Collections.emptyList();
}
一些会随业务变化的动态配置,都非常适合使用多级缓存,让你的程序稳稳当当哟~
没有试过的小伙伴,一定要去试一试O(∩_∩)O