当前位置: 首页 > 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/265489.html

相关文章:

  • 赣州网站建设联系方式全网整合营销公司
  • 电子毕业设计代做网站廊坊seo排名扣费
  • 做外汇网站代理赚钱吗行业关键词
  • wordpress管理员文件上海自动seo
  • 莱芜区都市网莱芜杂谈黑帽seo排名技术
  • 东莞网站搜索排名小红书推广费用一般多少
  • 沈阳网站制作聚艺科技品牌营销网站建设
  • 南昌网站建设公司网站建设公司奶茶软文案例300字
  • 哈尔滨建设局网站网站seo搜索引擎优化教程
  • 网站开发的付款方式山西百度查关键词排名
  • 淘宝客网站怎么做推广计划百度账号快速注册入口
  • 域名备案 个人 网站基本信息查询杭州搜索引擎优化公司
  • 做关键词优化需要修改网站标题整站seo定制
  • 省住房和城乡建设厅网站今日头条淄博新闻
  • 网站建设登录结构图营销方式都有哪些
  • 2345网址导航浏览器下载安装谷歌seo是什么
  • 网站推广赚钱吗汕头百度网站排名
  • 建设一个网站要多少费用百度指数查询手机版
  • 外贸网站建设公司价格武汉seo网站优化排名
  • seo整站优化技术培训英文网站seo
  • 成都网站建设公司官网百度竞价开户渠道
  • 怎么做网站的后台互联网营销的特点
  • 上海广告网站建设衡阳百度推广公司
  • 自己建免费网站漂亮的网页设计
  • 重庆网站建设行业新闻百度关键词怎么排名
  • 网页设计的通用规则有哪些淘宝seo优化
  • 河南省建协网官方网站宁波企业seo外包
  • 淘宝上做淘宝客的网站电商网站建设方案
  • 网站是广西住房和城乡建设厅京津冀协同发展
  • 西安新能源网站建设优化网站推广教程排名