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

安徽省交通运输厅门户网站太原seo网站排名

安徽省交通运输厅门户网站,太原seo网站排名,做网站如何注意排版问题,做网站要备案吗缓存 缓存:数据交换的缓冲区,存储数据的临时地方,读写性能高。 优点:降低后端负载、提高读写效率缺点:缓存往往需要保证数据的一致性,就需要代码去维护缓存的一致性 缓存更新策略 业务场景:…

缓存

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

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

缓存更新策略

在这里插入图片描述

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

主动更新

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

相关文章:

  • 网站建设的论文360排名优化工具
  • 东莞市企业网站建设平台怎样做引流推广
  • wp网站做企业站好不好2020年十大关键词
  • flash做网站步骤企业网站推广方案策划
  • 三水做网站百度seo怎么操作
  • 网站怎么添加关键词 好让百度收入_ 现在网站用的是模板做的酒店seo是什么意思
  • 门户网站建设收费小红书推广平台
  • 容桂销售型网站建设线上营销策略
  • 合肥 电子商务 网站建设万网是什么网站
  • 找第三方做网站 需要注意爱站网官网关键词
  • 市住房建设委官方网站搜狗引擎
  • 自己动手建设公司门户网站本网站三天换一次域名
  • 网站建设基本流程是什么怎么建网站详细步骤
  • 网站设计欣赏国外广州公司关键词网络推广
  • wordpress代码创建子站点信息流优化师证书
  • 佛山企业网站建设公司discuz论坛seo设置
  • 三维网站搭建教程求好用的seo软件
  • 连云港网站建设公司营销策略有哪些内容
  • 南京医院手机网站建设东莞网站营销策划
  • 网站设计证书上海百度seo牛巨微
  • 购物网站优化的建议站长之家素材网
  • 变化型网站怎样打百度人工客服热线
  • 外国的免费色情网站做公司网站的公司
  • 如何做营销型手机网站优化百度一下生活更好
  • 315网站专题怎么做关键词seo资源
  • 网站上做404页面怎样做郑州seo公司排名
  • 中国建设网官方网站6外贸接单十大网站
  • 网站建设脱颖而出郑州本地seo顾问
  • 网站建设需要学那些今日热点新闻视频
  • 基于互联网怎样做网站推广怎样把个人介绍放到百度