做网站保证效果,学历提升机构哪家好,能浏览外国网页的浏览器,wordpress精品模板Redis缓存的基本概念和使用 什么是缓存Redis缓存缓存更新策略缓存穿透缓存雪崩缓存击穿缓存工具类封装 什么是缓存 
缓存时数据交换的缓冲区#xff0c;存储数据的临时区#xff0c;读写性能较好。 例如计算机的三级缓存。CPU的计算速度超过内存的读写速度#xff0c;为了平… Redis缓存的基本概念和使用 什么是缓存Redis缓存缓存更新策略缓存穿透缓存雪崩缓存击穿缓存工具类封装 什么是缓存 
缓存时数据交换的缓冲区存储数据的临时区读写性能较好。 例如计算机的三级缓存。CPU的计算速度超过内存的读写速度为了平衡两者速度使用了三级缓存把经常需要读写的数据放到三级缓存中。 例如DNS缓存浏览器缓存nginx缓存JVM进程缓存Redis缓存数据库缓存等。 缓存作用 1、降低后端负载。 2、提高读写效率降低响应时间。 缓存成本 1、缓存一致性。 2、复杂度。例如缓存穿透缓存击穿缓存雪崩等问题使代码复杂度提升。 3、运维成本。 
Redis缓存 
客户端先请求Redis缓存命中缓存则直接返回没有命中再请求数据库若数据不存在则返回404如果数据存在则把新数据写到Redis缓存中最后返回数据。 例如店铺信息缓存如下 
Service
public class ShopServiceImpl extends ServiceImplShopMapper, Shop implements IShopService {Autowiredprivate StringRedisTemplate stringRedisTemplate;Overridepublic Object queryById(Long id) {String key  CACHE_SHOP_KEY  id;String shopJson  stringRedisTemplate.opsForValue().get(key);if (! StrUtil.isEmpty(shopJson)) {Shop shop  JSONUtil.toBean(shopJson, Shop.class);return Result.ok(shop);}Shop shop  getById(id);if (shop  null) {return Result.fail(店铺不存在);}stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(shop));stringRedisTemplate.expire(key, CACHE_SHOP_TTL, TimeUnit.MINUTES);return Result.ok(shop);}
}缓存更新策略 
1、内存淘汰 使用Redis的内存淘汰策略内存不足时自己淘汰部分数据下次查询时更新缓存。 应用于一致性要求低几乎不需要维护成本的场景。 2、超时剔除 给数据添加过期时间到期后自动删除缓存下次查询时更新缓存。 应用于一致性要求一般维护成本较低的场景。 3、主动更新 编写业务逻辑在修改数据库数据时更新缓存。 适用一致性较好维护成本较高的场景。 
业务场景 低一致性需求使用内存淘汰比如店铺类型的查询缓存。 高一致性需求使用主动更新并添加超时剔除作为兜底例如店铺详情的缓存。 
主动更新策略有哪些 1、旁路缓存Cache Aside 缓存调用者在更新数据库时更新缓存。相对可控实际使用较多。 2、读穿/写穿Read/Write Through 缓存和数据库整合成一个服务调用者无需关心数据库和缓存一致性。 3、读后写Write Behind Caching 调用者只操作缓存其他线程异步地将数据从缓存持久化到数据库。 
旁路缓存具体方案 1、删除缓存还是更新缓存 使用删除缓存因为如果使用更新缓存每次更新数据库都要更新缓存如果中间没有读取那么无效写操作太多。 2、如何保证操作数据库和缓存的同时成功和失败 单体系统将数据库和缓存的操作放在一个事务中。 分布式系统使用TCC等分布式事务。 3、先操作缓存还是先操作数据库 根据更新数据库时间更慢删除缓存时间更快。 
如果先删缓存后更新数据库。 线程1刚把缓存删除还没有更新缓存线程2查询缓存未命中会查询数据库旧值并且更新到缓存中导致缓存数据是旧值。 
如果先更新数据库再删除缓存。 缓存失效线程1查询缓存未命中查询到数据库线程2刚好修改数据库并且删除缓存线程2将旧数据写入缓存。 发生概率较小因为写入缓存时间较短需要在线程1查询完数据库写入缓存前把线程2的更新数据库和删除缓存都完成概率较小。 
例如实现查询商铺的查询缓存添加主动更新和超时剔除策略。 1、查询时如果缓存未命中则查询数据库并且写入缓存设置超时时间。 2、更新时先修改数据库后删除缓存。 
Override
public Object queryById(Long id) {String key  CACHE_SHOP_KEY  id;String shopJson  stringRedisTemplate.opsForValue().get(key);if (! StrUtil.isEmpty(shopJson)) {Shop shop  JSONUtil.toBean(shopJson, Shop.class);return Result.ok(shop);}Shop shop  getById(id);if (shop  null) {return Result.fail(店铺不存在);}stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(shop), CACHE_SHOP_TTL, TimeUnit.MINUTES);return Result.ok(shop);
}Override
Transactional
public Result updateShop(Shop shop) {Long id  shop.getId();if (id  null) {return Result.fail(店铺id不能为空);}updateById(shop);String key  CACHE_SHOP_KEY  id;stringRedisTemplate.delete(key);return Result.ok();
}缓存穿透 
缓存穿透客户端请求的数据在缓存和数据库中都不存在缓存永远不会生效会打到数据库。 解决 1、缓存空对象。 优点实现简单。 缺点额外内存消耗可能有短期数据不一致。 2、布隆过滤器。 优点内存消耗更小。 缺点实现复杂存在误判不能解决删除元素问题。 
编码实现这里使用缓存空对象。 
Override
public Object queryById(Long id) {String key  CACHE_SHOP_KEY  id;String shopJson  stringRedisTemplate.opsForValue().get(key);if (! StrUtil.isNotBlank(shopJson)) {Shop shop  JSONUtil.toBean(shopJson, Shop.class);return Result.ok(shop);}//命中之前缓存的空字符串if (.equals(shopJson)) {return Result.fail(店铺不存在);}Shop shop  getById(id);if (shop  null) {//写入空值解决缓存穿透stringRedisTemplate.opsForValue().set(key, , CACHE_NULL_TTL, TimeUnit.MINUTES);return Result.fail(店铺不存在);}stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(shop), CACHE_SHOP_TTL, TimeUnit.MINUTES);return Result.ok(shop);
}缓存雪崩 
缓存雪崩同一时段大量缓存key同时失效或者Redis服务宕机导致大量请求达到数据库。 解决 1、给不同key的过期时间ttl设置随机值。 2、使用Redis集群提高服务可用性。 3、添加降级限流策略。 4、添加多级缓存。 
缓存击穿 
缓存击穿热点key突然失效无数请求瞬间达到数据库带来巨大冲击。 解决 1、互斥锁。重建缓存时使用一个锁保证只有一个线程能重建缓存。可以结合两次if判断进行双重检查防止后面线程获取锁的开销。 优点实现简单保证一致性。 缺点性能受影响可能有死锁风险。 2、逻辑过期。即不设置ttlkey永不过期但是在value中设置expire字段。当线程1发现key过期时拿到互斥锁并开启新线程重建缓存。其他线程获取锁失败时直接拿旧数据。 优点线程无需等待性能较好。 缺点一致性更低有额外内存消耗。 区别逻辑过期也使用了互斥锁但是它和互斥锁的区别在于过期数据依然在Redis中因此其他线程可以先使用旧数据。 
例子根据id查询商铺使用互斥锁解决缓存击穿。 public Shop queryWithMutex(Long id) {String key  CACHE_SHOP_KEY  id;String shopJson  stringRedisTemplate.opsForValue().get(key);if (StrUtil.isNotBlank(shopJson)) {return JSONUtil.toBean(shopJson, Shop.class);}//命中之前缓存的空字符串解决缓存穿透if (.equals(shopJson)) {return null;}Shop shop  null;String lockKey  LOCK_SHOP_KEY  id;try {while (!tryLock(lockKey)) {Thread.sleep(50);}//双重检查shopJson  stringRedisTemplate.opsForValue().get(key);if (StrUtil.isNotBlank(shopJson)) {return JSONUtil.toBean(shopJson, Shop.class);}//命中之前缓存的空字符串解决缓存穿透if (.equals(shopJson)) {return null;}shop  getById(id);if (shop  null) {//写入空值解决缓存穿透stringRedisTemplate.opsForValue().set(key, , CACHE_NULL_TTL, TimeUnit.MINUTES);return null;}stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(shop), CACHE_SHOP_TTL, TimeUnit.MINUTES);} catch (InterruptedException e) {throw new RuntimeException(e);} finally {unLock(lockKey);}return shop;}例子根据id查询商铺逻辑过期解决缓存击穿例子 
public class RedisData {private LocalDateTime expireTime;private Object data;
}
public void saveShop2Redis(Long id, Long expireSeconds) {Shop shop  getById(id);RedisData redisData  new RedisData();redisData.setData(shop);redisData.setExpireTime(LocalDateTime.now().plusSeconds(expireSeconds));stringRedisTemplate.opsForValue().set(CACHE_SHOP_KEY  id, JSONUtil.toJsonStr(redisData));
}//逻辑过期解决缓存击穿
public Shop queryWithLogicalExpire(Long id) {String key  CACHE_SHOP_KEY  id;String shopJson  stringRedisTemplate.opsForValue().get(key);if (StrUtil.isBlank(shopJson)) {return null;}RedisData redisData  JSONUtil.toBean(shopJson, RedisData.class);LocalDateTime expireTime  redisData.getExpireTime();JSONObject jsonObject  (JSONObject) redisData.getData();Shop shop  JSONUtil.toBean(jsonObject, Shop.class);if (expireTime.isAfter(LocalDateTime.now())) {return shop;}String lockKey  LOCK_SHOP_KEY  id;if (tryLock(lockKey)) {CACHE_REBUILD_EXECUTOR.execute(()-{try {saveShop2Redis(id, 30L);} catch (Exception e) {throw new RuntimeException(e);} finally {unLock(lockKey);}});}return shop;
}缓存工具类封装 
缓存穿透缓存击穿缓存雪崩等业务逻辑较复杂如果每次使用缓存都需要写这部分代码可以考虑把这部分代码封装成一个工具类。 方法1将Java对象序列化成JSON字符串并且存储在string类型的Redis中并且可以设置过期时间。 方法2将Java对象序列化成JSON字符串并且存储在string类型的Redis中可以设置逻辑过期时间解决缓存击穿问题。 方法3根据指定key查询缓存并且反序列化成指定类型利用缓存空值的方式解决缓存穿透。 方法4根据指定key查询缓存并且反序列化成执行类型利用逻辑过期解决缓存击穿。 
Slf4j
Component
public class CacheClient {private final StringRedisTemplate stringRedisTemplate;public CacheClient(StringRedisTemplate stringRedisTemplate) {this.stringRedisTemplate  stringRedisTemplate;}private static final ExecutorService CACHE_REBUILD_EXECUTOR  Executors.newFixedThreadPool(10);public void set(String key, Object value, Long time, TimeUnit unit) {stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(value), time, unit);}public void setWithLogicalExpire(String key, Object value, Long time, TimeUnit unit) {RedisData redisData  new RedisData();redisData.setData(value);redisData.setExpireTime(LocalDateTime.now().plusSeconds(unit.toSeconds(time)));stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(redisData));}public R, ID R queryWithPassThrough(String keyPrefix, ID id, ClassR type, FunctionID, R dbFallBack,Long time, TimeUnit unit) {String key  keyPrefix  id;String json  stringRedisTemplate.opsForValue().get(key);if (StrUtil.isNotBlank(json)) {return JSONUtil.toBean(json, type);}if (.equals(json)) {return null;}R r  dbFallBack.apply(id);if (r  null) {//写入空值解决缓存穿透stringRedisTemplate.opsForValue().set(key, , CACHE_NULL_TTL, TimeUnit.MINUTES);return null;}set(key, r, time, unit);return r;}public R, ID R queryWithLogicalExpire(String keyPrefix, ID id, ClassR type, FunctionID, R dbFallBack,Long time, TimeUnit unit) {String key  keyPrefix  id;String json  stringRedisTemplate.opsForValue().get(key);if (StrUtil.isBlank(json)) {return null;}RedisData redisData  JSONUtil.toBean(json, RedisData.class);LocalDateTime expireTime  redisData.getExpireTime();JSONObject jsonObject  (JSONObject) redisData.getData();R r  JSONUtil.toBean(jsonObject, type);if (expireTime.isAfter(LocalDateTime.now())) {return r;}String lockKey  LOCK_SHOP_KEY  id;if (tryLock(lockKey)) {CACHE_REBUILD_EXECUTOR.execute(()-{try {R r1  dbFallBack.apply(id);setWithLogicalExpire(key, r1, time, unit);} catch (Exception e) {throw new RuntimeException(e);} finally {unLock(lockKey);}});}return r;}private boolean tryLock(String key) {Boolean flag  stringRedisTemplate.opsForValue().setIfAbsent(key, 1, LOCK_SHOP_TTL, TimeUnit.SECONDS);return BooleanUtil.isTrue(flag);}private void unLock(String key) {stringRedisTemplate.delete(key);}
}
 文章转载自: http://www.morning.bzsqr.cn.gov.cn.bzsqr.cn http://www.morning.lokext.com.gov.cn.lokext.com http://www.morning.kzcfp.cn.gov.cn.kzcfp.cn http://www.morning.yhwxn.cn.gov.cn.yhwxn.cn http://www.morning.smdnl.cn.gov.cn.smdnl.cn http://www.morning.npbnc.cn.gov.cn.npbnc.cn http://www.morning.yfqhc.cn.gov.cn.yfqhc.cn http://www.morning.ruifund.com.gov.cn.ruifund.com http://www.morning.rnlx.cn.gov.cn.rnlx.cn http://www.morning.bpcf.cn.gov.cn.bpcf.cn http://www.morning.xrpwk.cn.gov.cn.xrpwk.cn http://www.morning.fldk.cn.gov.cn.fldk.cn http://www.morning.qrsm.cn.gov.cn.qrsm.cn http://www.morning.ykrg.cn.gov.cn.ykrg.cn http://www.morning.nlzpj.cn.gov.cn.nlzpj.cn http://www.morning.nfbxgtj.com.gov.cn.nfbxgtj.com http://www.morning.lfxcj.cn.gov.cn.lfxcj.cn http://www.morning.jwtjf.cn.gov.cn.jwtjf.cn http://www.morning.kqzt.cn.gov.cn.kqzt.cn http://www.morning.lwcgh.cn.gov.cn.lwcgh.cn http://www.morning.zbgqt.cn.gov.cn.zbgqt.cn http://www.morning.tsnmt.cn.gov.cn.tsnmt.cn http://www.morning.lsfbb.cn.gov.cn.lsfbb.cn http://www.morning.xbxks.cn.gov.cn.xbxks.cn http://www.morning.qxgmp.cn.gov.cn.qxgmp.cn http://www.morning.ujianji.com.gov.cn.ujianji.com http://www.morning.lmrcq.cn.gov.cn.lmrcq.cn http://www.morning.dhyzr.cn.gov.cn.dhyzr.cn http://www.morning.msbmp.cn.gov.cn.msbmp.cn http://www.morning.wtcyz.cn.gov.cn.wtcyz.cn http://www.morning.psxwc.cn.gov.cn.psxwc.cn http://www.morning.lqgfm.cn.gov.cn.lqgfm.cn http://www.morning.cwjxg.cn.gov.cn.cwjxg.cn http://www.morning.tnbsh.cn.gov.cn.tnbsh.cn http://www.morning.rmxwm.cn.gov.cn.rmxwm.cn http://www.morning.bwzzt.cn.gov.cn.bwzzt.cn http://www.morning.lydtr.cn.gov.cn.lydtr.cn http://www.morning.zwhtr.cn.gov.cn.zwhtr.cn http://www.morning.lmtbl.cn.gov.cn.lmtbl.cn http://www.morning.zrkws.cn.gov.cn.zrkws.cn http://www.morning.mftzm.cn.gov.cn.mftzm.cn http://www.morning.hzqjgas.com.gov.cn.hzqjgas.com http://www.morning.21r000.cn.gov.cn.21r000.cn http://www.morning.yzdth.cn.gov.cn.yzdth.cn http://www.morning.bpmfg.cn.gov.cn.bpmfg.cn http://www.morning.rfbq.cn.gov.cn.rfbq.cn http://www.morning.fqmbt.cn.gov.cn.fqmbt.cn http://www.morning.drhnj.cn.gov.cn.drhnj.cn http://www.morning.tfwg.cn.gov.cn.tfwg.cn http://www.morning.tnfyj.cn.gov.cn.tnfyj.cn http://www.morning.pcrzf.cn.gov.cn.pcrzf.cn http://www.morning.smdnl.cn.gov.cn.smdnl.cn http://www.morning.drnfc.cn.gov.cn.drnfc.cn http://www.morning.rhkq.cn.gov.cn.rhkq.cn http://www.morning.ljxps.cn.gov.cn.ljxps.cn http://www.morning.snlxb.cn.gov.cn.snlxb.cn http://www.morning.ryqsq.cn.gov.cn.ryqsq.cn http://www.morning.kqxng.cn.gov.cn.kqxng.cn http://www.morning.mtrfz.cn.gov.cn.mtrfz.cn http://www.morning.ydrfl.cn.gov.cn.ydrfl.cn http://www.morning.jtsdk.cn.gov.cn.jtsdk.cn http://www.morning.zcxjg.cn.gov.cn.zcxjg.cn http://www.morning.bmtyn.cn.gov.cn.bmtyn.cn http://www.morning.dzrcj.cn.gov.cn.dzrcj.cn http://www.morning.dtgjt.cn.gov.cn.dtgjt.cn http://www.morning.bpmtq.cn.gov.cn.bpmtq.cn http://www.morning.kfbth.cn.gov.cn.kfbth.cn http://www.morning.jtdrz.cn.gov.cn.jtdrz.cn http://www.morning.dgsr.cn.gov.cn.dgsr.cn http://www.morning.qhkdt.cn.gov.cn.qhkdt.cn http://www.morning.hxhrg.cn.gov.cn.hxhrg.cn http://www.morning.wnxqf.cn.gov.cn.wnxqf.cn http://www.morning.lswgs.cn.gov.cn.lswgs.cn http://www.morning.jnkng.cn.gov.cn.jnkng.cn http://www.morning.wdjcr.cn.gov.cn.wdjcr.cn http://www.morning.hgsylxs.com.gov.cn.hgsylxs.com http://www.morning.trmpj.cn.gov.cn.trmpj.cn http://www.morning.tmbfz.cn.gov.cn.tmbfz.cn http://www.morning.nfbnl.cn.gov.cn.nfbnl.cn http://www.morning.hchrb.cn.gov.cn.hchrb.cn