当前位置: 首页 > news >正文

山东兴华建设集团有限公司网站拼音漳州正规网站建设

山东兴华建设集团有限公司网站拼音,漳州正规网站建设,现在建设一个网站多少钱,网站查询域名解析ip缓存 缓存:数据交换的缓冲区,存储数据的临时地方,读写性能高。 优点:降低后端负载、提高读写效率缺点:缓存往往需要保证数据的一致性,就需要代码去维护缓存的一致性 缓存更新策略 业务场景:…

缓存

缓存:数据交换的缓冲区,存储数据的临时地方,读写性能高。
在这里插入图片描述

  • 优点:降低后端负载、提高读写效率
  • 缺点:缓存往往需要保证数据的一致性,就需要代码去维护缓存的一致性

缓存更新策略

在这里插入图片描述

业务场景:
低一致性需求:使用内存淘汰机制
高一致性需求:主动更新,以超时剔除为兜底方案

主动更新

  1. 自己写代码,在更新数据库的同时更新缓存
  2. 缓存与数据库整合为一个服务,由服务来维护一致性,调用者调用该服务,无需关心缓存的一致性问题
  3. 调用者只操作缓存,由其他线程异步的将缓存数据持久化到数据库,保证最终一致性

缓存穿透解决方案:缓存空对象

在这里插入图片描述

/*** 缓存穿透:缓存空对象* @param id* @return*/public Shop queryWithPassThrough(Long id) {// 从redis中查缓存String shopKey = RedisConstants.CACHE_SHOP_KEY + id;String shopJson = stringRedisTemplate.opsForValue().get(shopKey);if(StrUtil.isNotBlank(shopJson)) {// 存在 - 直接返回return JSONUtil.toBean(shopJson, Shop.class);}// 判断命中的是否是空值Assert.isTrue(shopJson == null , "店铺不存在");// 不存在 - 操作数据库 - 写入redisShop shop = getById(id);if(shop == null) {stringRedisTemplate.opsForValue().set(shopKey, "", RedisConstants.CACHE_NULL_TTL, TimeUnit.MINUTES);throw new RuntimeException("店铺不存在");}stringRedisTemplate.opsForValue().set(shopKey, JSONUtil.toJsonStr(shop), RedisConstants.CACHE_SHOP_TTL, TimeUnit.MINUTES);return shop;}

缓存击穿解决方案:互斥锁(setnx)

在这里插入图片描述
一个线程在请求redis的数据时,先尝试去获取锁;只有获取到锁的线程才能去执行相应的操作,如果获取不到锁,只能阻塞等待。

为了防止服务出现故障时导致锁无法释放,所以一般设置锁的时候会加一个过期时间。

 /*** 获取锁* @param key* @return*/private boolean tryLock(String key) {Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", 10, TimeUnit.SECONDS); // 10秒锁return BooleanUtil.isTrue(flag);}/*** 释放锁* @param key*/private void unLock(String key) {stringRedisTemplate.delete(key);}/**
* 缓存击穿(互斥锁setnx) + 缓存穿透
* @param id
* @return
*/
public Shop queryWithMutex(Long id) {// 从redis中查缓存String shopKey = RedisConstants.CACHE_SHOP_KEY + id;String shopJson = stringRedisTemplate.opsForValue().get(shopKey);if(StrUtil.isNotBlank(shopJson)) { // 只有是有字符串的时候才会是true// 存在 - 直接返回return JSONUtil.toBean(shopJson, Shop.class);}// 判断命中的是否是空值Assert.isTrue(shopJson == null , "店铺不存在");// 实现缓存重建Shop shop = null;// 获取互斥锁String lockKey = RedisConstants.LOCK_SHOP_KEY + id;try {boolean isLock = tryLock(lockKey);// 获取失败 - 休眠、重试if(!isLock) {Thread.sleep(50);queryWithMutex(id); // 递归、再次调用}// 获取成功 - 根据id查询数据库// 不存在 - 操作数据库 - 写入redisThread.sleep(200);shop = getById(id);if(shop == null) {stringRedisTemplate.opsForValue().set(shopKey, "", RedisConstants.CACHE_NULL_TTL, TimeUnit.MINUTES); // 店铺不存在 - 设置空值throw new RuntimeException("店铺不存在");}stringRedisTemplate.opsForValue().set(shopKey, JSONUtil.toJsonStr(shop), RedisConstants.CACHE_SHOP_TTL, TimeUnit.MINUTES);} catch (InterruptedException e) {throw new RuntimeException(e);} finally {unLock(lockKey); // 释放锁}return shop;
}

缓存击穿解决方案:互斥锁 + 逻辑过期

在这里插入图片描述

/*** 缓存击穿(互斥锁setnx、逻辑过期)* @param id* @return*/
public Shop queryWithLogicalExpire(Long id) {// 从redis中查缓存String shopKey = RedisConstants.CACHE_SHOP_KEY + id;String redisDataJson = stringRedisTemplate.opsForValue().get(shopKey);/*if(StrUtil.isBlank(redisDataJson)) { // 不存在return null;}*/RedisData<Shop> redisData = JSONUtil.toBean(redisDataJson, new TypeReference<RedisData<Shop>>() {}, false);// 未过期、直接返回if(redisData != null && redisData.getExpireTime().isAfter(LocalDateTime.now())) {return redisData.getData();}// 实现缓存重建Shop shop = redisData.getData();// 获取互斥锁String lockKey = RedisConstants.LOCK_SHOP_KEY + id;boolean isLock = tryLock(lockKey);// 获取失败 - 返回旧数据if(!isLock) {return shop;}// 获取成功 - 开启独立线程 - 实现缓存重建 - 暂时返回旧的店铺信息CACHE_REBUILD_EXECUTOR.submit(()->{ // 开启独立线程try {saveShopToRedis(id, 30L); // 实现缓存重建} catch (Exception e) {throw new RuntimeException(e);} finally {unLock(lockKey); // 释放锁}});return shop;
}

这里封装RedisData对象的时候也有个技巧,为了不破坏原本Shop的类型,所以决定使用组合的方式,让Shop称为RedisData的成员:

@Data
@Accessors(chain = true)
public class RedisData<T> {private LocalDateTime expireTime;private T data; // 这样可以避免对原来的数据做修改
}

缓存工具封装

@Slf4j
@Component
public class CacheClient {@Resourceprivate StringRedisTemplate stringRedisTemplate;/*** 设置过期时间* @param key* @param value* @param expireTime* @param timeUnit* @param <T>*/public <T> void set(String key, T value, Long expireTime, TimeUnit timeUnit) {stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(value), expireTime, timeUnit);}/*** 设置逻辑过期时间* @param key* @param data* @param logicalExpireTime* @param timeUnit* @param <T>*/public <T> void setWithLogicalExpire(String key, T data, Long logicalExpireTime, TimeUnit timeUnit) {RedisData<T> redisData = new RedisData<T>().setExpireTime(LocalDateTime.now().plusSeconds(timeUnit.toSeconds(logicalExpireTime))).setData(data);stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(redisData));}/*** 缓存穿透解决方案封装:缓存空对象* 调用:cacheClient.queryWithPassThrough(RedisConstants.CACHE_SHOP_KEY, id, Shop.class, this::getById, RedisConstants.CACHE_SHOP_TTL, TimeUnit.MINUTES);* @param keyPrefix* @param id* @param type 返回的数据类型* @param dbFallback:Function<ID, R> dbFallback函数式编程——Function<参数, 返回值>* @param expireTime* @param timeUnit* @return* @param <R>* @param <ID>*/public <R, ID> R queryWithPassThrough(String keyPrefix, ID id, Class<R> type, Function<ID, R> dbFallback, Long expireTime, TimeUnit timeUnit) {String key = keyPrefix + id;// 从redis中查缓存String json = stringRedisTemplate.opsForValue().get(key);if(StrUtil.isNotBlank(json)) {// 存在 - 直接返回return JSONUtil.toBean(json, type);}// 判断命中的是否是空值Assert.isTrue(json == null , "店铺不存在");// 不存在 - 操作数据库 - 写入redisR r = dbFallback.apply(id);if(r == null) {stringRedisTemplate.opsForValue().set(key, "", RedisConstants.CACHE_NULL_TTL, TimeUnit.MINUTES);throw new RuntimeException("店铺不存在");}set(key, r, expireTime, timeUnit);return r;}/*** 获取锁* @param key* @return*/private boolean tryLock(String key) {Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", 10, TimeUnit.SECONDS); // 10秒锁return BooleanUtil.isTrue(flag);}/*** 释放锁* @param key*/private void unLock(String key) {stringRedisTemplate.delete(key);}private static final ExecutorService CACHE_REBUILD_EXECUTOR = Executors.newFixedThreadPool(10);/*** 缓存击穿(互斥锁setnx、逻辑过期)* 调用:cacheClient.queryWithLogicalExpire(RedisConstants.CACHE_SHOP_KEY, id, Shop.class, this::getById, RedisConstants.CACHE_SHOP_TTL, TimeUnit.SECONDS);* @param keyPrefix* @param id* @param type* @param dbFallback* @param expireTime* @param timeUnit* @return* @param <R>* @param <ID>*/public <R, ID> R queryWithLogicalExpire(String keyPrefix, ID id, Class<R> type, Function<ID, R> dbFallback, Long expireTime, TimeUnit timeUnit) {// 从redis中查缓存String key = keyPrefix + id;String redisDataJson = stringRedisTemplate.opsForValue().get(key);RedisData<R> redisData = JSONUtil.toBean(redisDataJson, new TypeReference<RedisData<R>>() {}, false);// 未过期、直接返回R rOld = JSONUtil.toBean((JSONObject) redisData.getData(), type);if(redisData != null && redisData.getExpireTime().isAfter(LocalDateTime.now())) {return rOld;}// 实现缓存重建// 获取互斥锁String lockKey = RedisConstants.LOCK_SHOP_KEY + id;boolean isLock = tryLock(lockKey);// 获取失败 - 返回旧数据if(!isLock) {return rOld;}// 获取成功 - 开启独立线程 - 实现缓存重建 - 暂时返回旧的店铺信息CACHE_REBUILD_EXECUTOR.submit(()->{ // 开启独立线程try {// 重建缓存 - 查询数据库R rNew = dbFallback.apply(id);this.setWithLogicalExpire(key, rNew, expireTime, timeUnit);} catch (Exception e) {throw new RuntimeException(e);} finally {unLock(lockKey); // 释放锁}});return rOld;}
}
http://www.dtcms.com/a/402869.html

相关文章:

  • 潍坊行业网站检察机门户网站建设自查报告
  • 网站关键词库怎么做分享网站友情链接
  • 家具能在什么网站上做免费咨询在线医生
  • 河北建设厅网站登陆怎么找附件黄岛网站建设哪家权威
  • 建设通网站是做什么的外链网站有哪些
  • 自然堂网站建设策划书电脑和手机同步编辑wordpress
  • 请人建设网站需要注意什么wordpress 管理员权限设置
  • 男女做暖网站国外图片网站源码
  • 工程项目网站wordpress模板製作
  • 做网站需要跟客户了解什么软件家在深圳 歌曲
  • 关于网站建设的调研报告企业seo解决方案
  • 网站开发网络公司兼职百度怎么免费推广自己的产品
  • 网站策划书案例展示游戏网站规划方案
  • 如何在年报网站上做遗失公告樱花16q808a
  • 郴州网站建设解决方案网站 免费 托管运营
  • 网站关键字没有排名素材网站 国外
  • 游戏自助充值网站怎么做网站开发目标开题报告
  • 宁波网站怎么建设wordpress访问过的页码不变色
  • 三门峡做网站的公司建设网站南沙
  • 网站怎样做301购物网站建站系统
  • 如何自建网站?贵州最近爆发的传染病
  • 新郑市住房建设局网站网站建设Skype打不开
  • 专业网站设计制作优化排名ui设计30岁后的出路
  • 做儿童方面的网站wordpress大前端dux5.2
  • 企业网站设计策划案信用网站标准化建设模块都有哪些
  • 有关网站建设有那些功能wordpress爆破软件
  • 做网站的有哪些公司网站开发需不需要考研
  • 自已建网站微信登录网站logo设计教程
  • 赤峰网站建设建站公司企业门户网站运营推广
  • 如何用WordPress建小说站wordpress怎么做301重定向