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

安徽省交通运输厅网站新闻小学生摘抄

安徽省交通运输厅网站,新闻小学生摘抄,网站建设狼雨,中铁三局招聘是什么梗缓存 缓存:数据交换的缓冲区,存储数据的临时地方,读写性能高。 优点:降低后端负载、提高读写效率缺点:缓存往往需要保证数据的一致性,就需要代码去维护缓存的一致性 缓存更新策略 业务场景:…

缓存

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

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

缓存更新策略

在这里插入图片描述

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

主动更新

  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/wzjs/129433.html

相关文章:

  • 嘉兴做企业网站的公司怎么做好推广
  • wordpress 免费中文模板下载seo厂商
  • 封丘县建设银行网站上海百度推广客服电话
  • 南通网站制作设计热点新闻事件及观点
  • 电商网站在线支付怎么做建立网站的步骤
  • 织梦怎么修改网站logoseo诊断工具有哪些
  • 新网 网站建立杭州网站
  • 杭州做网站 做小程序seo基本概念
  • 电脑可以做网站主机么自己个人怎样做电商
  • 国外 网站 欣赏网站推广的意义和方法
  • vvic网站一起做网店手机在线制作网站
  • 重庆工程建设信息查询seo推广是什么意思
  • 哈密地网站建设百度一下官方下载安装
  • 网站建设开发数据库南通百度网站快速优化
  • 如何编写一套网站模板关键词分为哪几类
  • 古冶区城乡建设局网站网络推广的工作好做吗
  • 网站开发运营公司绩效提成方案上海网站建设推广服务
  • 为什么网站数量减少淘宝指数查询官网
  • 创立一个网站需要什么浙江新手网络推广
  • 常州工厂网站建设武安百度seo
  • 母婴网站设计开发微信营销软件
  • 企业网站一般包括哪些内容网站建设产品介绍
  • 代做课件的网站南宁seo服务优化
  • 网站 栏目 英语哪里做网络推广好
  • 个人网站是否需要备案福州短视频seo平台
  • 网站设计是用什么做的国际新闻快报
  • 网站常见程序问题郑州网络推广
  • 网站建设小故事常州网络推广seo
  • 国家卫生和计划生育委员会湖南seo优化哪家好
  • 网上兼职做效果图网站有哪些抖音黑科技引流推广神器