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

安徽省交通运输厅金良群排名优化软件

安徽省交通运输厅金良,群排名优化软件,the7企业中 英文wordpress模板,网站开发工具 n缓存 缓存:数据交换的缓冲区,存储数据的临时地方,读写性能高。 优点:降低后端负载、提高读写效率缺点:缓存往往需要保证数据的一致性,就需要代码去维护缓存的一致性 缓存更新策略 业务场景:…

缓存

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

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

缓存更新策略

在这里插入图片描述

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

主动更新

  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/391384.html

相关文章:

  • 长沙企业网站制作百度小说
  • 独立网站推广公司快速seo软件
  • 旅游网站建设推广网络推广加盟
  • 网站建设案例渠道怎么做电商新手入门
  • 汕头网站建设推广价格识图搜索在线 照片识别
  • 太原有网站工程公司吗无锡百度竞价
  • 洞口做网站如何做免费网站推广
  • 怎么将国内网站接入香港服务器内容营销案例
  • pycharm网站开发广州关键词排名推广
  • 有赞网站开发精准客源推广引流
  • 常州网站建设平台企业邮箱注册申请
  • 株洲网站设计seo从入门到精通
  • 鞋材 东莞网站建设鸣蝉智能建站
  • 舞钢做网站seo的全称是什么
  • 南宁网站建设加q.479185700长沙有实力seo优化公司
  • wordpress占用服务器内存代码优化
  • 电商网站建设方案模板昆明网络推广
  • 平度市城市建设局网站人员优化方案怎么写
  • 苏州 网站设计网站的营销推广方案
  • 阿里云可以做电影网站吗昆明百度关键词优化
  • 免费网站推荐软件杭州网站优化方案
  • 想要找个网站做环评公示网络营销的市场背景
  • 做网店哪个网站好海外新闻发布
  • 桂林生活网新闻中心怎么进行seo
  • 怎么用wordpress做搜索网站北京疫情最新消息
  • 网站开发程序设计全网营销与seo
  • 高新区建设局网站网站服务器查询工具
  • 中国建设教育协会网站查询网络营销论文
  • 虎门企业网站建设公司网游推广
  • phpcms 做好网站怎么保存哈尔滨seo关键词排名