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

北京改网站wordpress添加邀请码输入框

北京改网站,wordpress添加邀请码输入框,做直播券的网站有多少钱,wordpress获取page id黑马点评缓存部分: 缓存的标准操作方式就是查询数据库之前先查询缓存,如果缓存数据存在,则直接从缓存中返回,如果缓存数据不存在,再查询数据库,然后将数据存入redis 最初缓存版本 //最初源代码public Sh…

黑马点评缓存部分:

缓存的标准操作方式就是查询数据库之前先查询缓存,如果缓存数据存在,则直接从缓存中返回,如果缓存数据不存在,再查询数据库,然后将数据存入redis

最初缓存版本

//最初源代码public Shop queryWithPassThrough(Long id) {//从redis中查商铺缓存String shopJson = stringRedisTemplate.opsForValue().get(SHOP_ID + id);//判断是否存在if (StrUtil.isNotBlank(shopJson)) {Shop shop = JSONUtil.toBean(shopJson, Shop.class);//存在 直接返回return null;}//判断是不是空if (shopJson != null) {//返回错误信息return null;}//不存在  查数据库 MybatisPlusShop shop = getById(id);//数据库不存在 返回错误if (shop == null) {//将空值写入redisstringRedisTemplate.opsForValue().set(SHOP_ID + id, "", CACHE_NULL_TTL, TimeUnit.MINUTES);return null;}//数据库中存在  写入redisstringRedisTemplate.opsForValue().set(SHOP_ID + id, JSONUtil.toJsonStr(shop), CACHE_SHOP_TTL, TimeUnit.MINUTES);//返回return null;}

解决数据不一致的办法:双写

由于我们的缓存的数据源来自于数据库,而数据库的数据是会发生变化的,因此,如果当数据库中数据发生变化,而缓存却没有同步,此时就会有一致性问题存在, 几种方案

Cache Aside Pattern 人工编码方式:缓存调用者在更新完数据库后再去更新缓存,也称之为双写方案

Read/Write Through Pattern : 由系统本身完成,数据库与缓存的问题交由系统本身去处理

Write Behind Caching Pattern :调用者只操作缓存,其他线程去异步处理数据库,实现最终一致 

如果采用第一个方案,那么假设我们每次操作数据库后,都操作缓存,但是中间如果没有人查询,那么这个更新动作实际上只有最后一次生效,中间的更新动作意义并不大,我们可以把缓存删除,等待再次查询时,将缓存中的数据加载出来

我们应当是先操作数据库,再删除缓存,原因在于,如果你选择先删除缓存再操作数据库,在两个线程并发来访问时,假设线程1先来,他先把缓存删了,此时线程2过来,他查询缓存数据并不存在,此时他写入缓存,当他写入缓存后,线程1再执行更新动作时,实际上写入的就是旧的数据,新的数据被旧数据覆盖了。

缓存穿透问题:缓存空对象

指客户端请求的数据在缓存中和数据库中都不存在,这样缓存永远不会生效,这些请求都会打到数据库。

缓存穿透的解决方案有哪些?

  • 缓存null值

  • 布隆过滤

  • 增强id的复杂度,避免被猜测id规律

  • 做好数据的基础格式校验

  • 加强用户权限校验

  • 做好热点参数的限流 

这里使用缓存null值来完成缓存穿透

public <R,ID> R queryWithPassThrough(String keyPrefix, ID id, Class<R> type, Function<ID, R> dbFallback, Long time, TimeUnit unit){String key = keyPrefix + id;// 1.从redis查询商铺缓存String json = stringRedisTemplate.opsForValue().get(key);// 2.判断是否存在if (StrUtil.isNotBlank(json)) {// 3.存在,直接返回return JSONUtil.toBean(json, type);}// 判断命中的是否是空值if (json != null) {// 返回一个错误信息return null;}// 4.不存在,根据id查询数据库R r = dbFallback.apply(id);// 5.不存在,返回错误if (r == null) {// 将空值写入redisstringRedisTemplate.opsForValue().set(key, "", CACHE_NULL_TTL, TimeUnit.MINUTES);// 返回错误信息return null;}// 6.存在,写入redisthis.set(key, r, time, unit);return r;}

缓存雪崩

热点Key问题

缓存雪崩是指在同一时段大量的缓存key同时失效或者Redis服务宕机,导致大量请求到达数据库,带来巨大压力。

解决方案:

  • 给不同的Key的TTL添加随机值

  • 利用Redis集群提高服务的可用性

  • 给缓存业务添加降级限流策略

  • 给业务添加多级缓存

缓存雪崩的解决方案:互斥锁

代码:

//互斥锁public Shop queryWithMutex(Long id) {//1.从redis中查商铺缓存String shopJson = stringRedisTemplate.opsForValue().get(SHOP_ID + id);//判断是否存在if (StrUtil.isNotBlank(shopJson)) {//存在 直接返回return JSONUtil.toBean(shopJson, Shop.class);}//判断是不是空if (shopJson != null) {//返回错误信息return null;}//实现缓存重建// 获取互斥锁String lockKey = SHOP_LOCK_KEY + id;Shop shop = null;try {boolean isLock = tryLock(lockKey);// 判断是否成功if (!isLock) {//失败->休眠并重试Thread.sleep(50);queryWithMutex(id);}//成功->查询数据库信息shop = getById(id);//模拟延迟//Thread.sleep(200);//数据库不存在 返回错误if (shop == null) {//将空值写入redisstringRedisTemplate.opsForValue().set(SHOP_ID + id, "", CACHE_NULL_TTL, TimeUnit.MINUTES);return null;}//数据库中存在  写入redisstringRedisTemplate.opsForValue().set(SHOP_ID + id, JSONUtil.toJsonStr(shop), CACHE_SHOP_TTL, TimeUnit.MINUTES);//释放互斥锁} catch (InterruptedException e) {throw new RuntimeException(e);} finally {unLock(lockKey);}//返回return shop;}// 获取锁private boolean tryLock(String key) {Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", LOCK_SHOP_TTL, TimeUnit.SECONDS);//返回的啥时候拆箱 可能出现空指针return BooleanUtil.isTrue(flag);}//释放锁private void unLock(String key) {stringRedisTemplate.delete(key);}

互斥锁的大致逻辑:

缓存查询+缓存重建

先从 Redis 中查询缓存数据。

如果缓存命中(数据存在),直接返回缓存数据。

如果缓存未命中(数据不存在),尝试获取分布式锁,防止多个线程同时重建缓存。

如果没有得到锁,休眠 重试

如果得到了锁 查询数据库信息 

数据库信息不存在 将空值写入redis 

数据库中存在 写入redis

最后无论如何释放锁 返回数据

 DoubleCheck: 为了避免多个线程同时重建缓存,从而减少不必要的数据库查询和缓存写入操作

在这里需要加入这个doubleCheck

在获取锁之前,先检查一次缓存(第一次检查)。如果缓存已经命中,直接返回数据,无需获取锁。这样可以避免大量线程同时竞争锁,减少锁的开销。

在获取锁之后、查询数据库之前,再次检查缓存(第二次检查)。如果缓存已经命中,直接返回数据,无需查询数据库。这样可以确保在等待锁的过程中,其他线程没有已经重建缓存,从而避免重复操作。(上述代码没写,逻辑过期的写了)

第二次检查防止数据更新,即使锁了,其他线程或进程仍然可以 通过其他途径更新缓存,而不受当前锁的限制

缓存雪崩的解决方案:逻辑过期

 private static final ExecutorService CACHE_REBUILD_EXECUTOR = Executors.newFixedThreadPool(10);//热点key 逻辑public Shop queryWithLogicalExpire(Long id) {String key = CACHE_SHOP_KEY + id;String json = stringRedisTemplate.opsForValue().get(key);//从redis中查商铺缓存String shopJson = stringRedisTemplate.opsForValue().get(SHOP_ID + id);//判断是否存在if (StrUtil.isBlank(shopJson)) {//存在 直接返回return null;}//命中 需要把json反序列化为对象RedisData redisData = JSONUtil.toBean(json, RedisData.class);//redisData.getData()是一个Object对象 需要,但实际上是一个JSONObjectShop shop = JSONUtil.toBean((JSONObject) redisData.getData(), Shop.class);LocalDateTime expireTime = redisData.getExpireTime();//判断过期if (expireTime.isAfter(LocalDateTime.now())) {//没过期return shop;}//过期了需要缓存重建String lockKey = LOCK_SHOP_KEY + id;boolean isLock = tryLock(lockKey);//判断是否获取锁成功if (isLock) {//!!!做DoubleCheckString jsonAgain = stringRedisTemplate.opsForValue().get(key);if (StrUtil.isNotBlank(jsonAgain)) {RedisData redisDataAgain = JSONUtil.toBean(jsonAgain, RedisData.class);LocalDateTime expireTimeAgain = redisDataAgain.getExpireTime();if (expireTimeAgain.isAfter(LocalDateTime.now())) {return JSONUtil.toBean((JSONObject) redisDataAgain.getData(), Shop.class);}}// 缓存仍需重建,异步执行CACHE_REBUILD_EXECUTOR.submit(() -> {try {this.saveShop2Redis(id, 20L);} catch (Exception e) {throw new RuntimeException(e);} finally {unLock(lockKey);}});}return shop;}
//缓存重建
public void saveShop2Redis(Long id, Long expireSeconds) {//查询店铺信息Shop shop = getById(id);//封装逻辑过期时间RedisData redisData = new RedisData();redisData.setData(shop);redisData.setExpireTime(LocalDateTime.now().plusSeconds(expireSeconds));//写入RedisstringRedisTemplate.opsForValue().set(CACHE_SHOP_KEY + id, JSONUtil.toJsonStr(redisData));}

实现逻辑过期,需要创建一个逻辑过期数据类 记录过期时间的一个类

@Data
public class RedisData {private LocalDateTime expireTime;private Object data;
}

逻辑:

和前面一样,先从redis中查缓存 存在就返回数据

在返回数据的时候 先将json反序列化成RedisData对象 再将RedisData 中的data转换成shop,真正需要的对象

然后先判断数据是否过期 没过期返回

过期了需要进行缓存重建

重建还是先拿到锁,再做DoubleCheck 如果不需要继续重建,返回对象

如果确实需要重建 这里采用的是异步执行 

        1、使用线程池 CACHE_REBUILD_EXECUTOR 提交一个异步任务。

        2、在异步任务中,调用 saveShop2Redis 方法重建缓存。

        3、无论缓存重建是否成功,最终都会释放锁。

最后返回数据

值得注意的是,在执行之前需要对缓存数据进行预热 将数据加载到缓存中。


文章转载自:

http://WTVIbI48.hqnsf.cn
http://Xwlsg1h2.hqnsf.cn
http://x91Hnqif.hqnsf.cn
http://OEJPgjF0.hqnsf.cn
http://JwsN8uqN.hqnsf.cn
http://SqJj1V3i.hqnsf.cn
http://Ir4GKcm9.hqnsf.cn
http://UjG8XWb0.hqnsf.cn
http://WWr22Y1p.hqnsf.cn
http://rCRr6GtQ.hqnsf.cn
http://XnLzHJtp.hqnsf.cn
http://KQodCI9e.hqnsf.cn
http://2nC1f2BO.hqnsf.cn
http://jdp7fQyS.hqnsf.cn
http://gbRsv7eZ.hqnsf.cn
http://Bp3yldpl.hqnsf.cn
http://bnMluSUZ.hqnsf.cn
http://a5obkPls.hqnsf.cn
http://5aUU0JPS.hqnsf.cn
http://diS3HGFQ.hqnsf.cn
http://rFRiwM6K.hqnsf.cn
http://nJyln6bG.hqnsf.cn
http://Bx8mO0ju.hqnsf.cn
http://K8pKf3YE.hqnsf.cn
http://ONLCH4lC.hqnsf.cn
http://8xV8iD4W.hqnsf.cn
http://WO7FyK8Q.hqnsf.cn
http://fHZVlQfL.hqnsf.cn
http://yMMFAGIF.hqnsf.cn
http://xhLwfOYK.hqnsf.cn
http://www.dtcms.com/wzjs/639275.html

相关文章:

  • 个人网站可以做资讯吗?外贸网站功能
  • 广水网站建设怎么做可以看外国视频网站
  • 关于.net网站开发外文书籍网站源码上传到哪个文件夹
  • 网站重定向北京网站seo
  • 湖北建设网站西楚房产网宿迁房产网
  • 建设银行官网网站首页友情链接多久有效果
  • 吉安手机网站建设hot插件 wordpress
  • 郑州专业公司网站建设公司做神马网站优化排名软
  • 石家庄网站建设技术支持广告商
  • 做外贸的网站域名怎么买wordpress怎么注册用户
  • 家里笔记本做网站 怎么解析网站建设功能的策划书
  • 广州市网站建设分站价格响应式网站 教程
  • 做网站一年网站建设名词解析
  • 桔子建站官网360百度网站怎么做
  • 临沂网站优化公司网站内页301
  • 重庆建设行业信息网站网站建设公司怎么挣钱
  • 跟知乎一样的网站网站制作与网站建设
  • 山东建设网站首页电商行业建设网站
  • 做网站的重点目标东莞是哪个省
  • wordpress网站静态化做网站策划一个专利的主题宣传
  • 合肥网站建设制作公司兰溪建设局网站
  • 茂名企业自助建站系统什么是单页网站
  • 软件开发公司网站模板wordpress创建文章
  • dw制作一个环保网站模板下载深圳专业做网站的公司哪家好
  • 广西壮族自治区建设厅网站福田庆三案例照
  • 网站没收录要怎么做北京工程信息网
  • 挂机赚一小时75元如何优化关键词排名到首页
  • 在线网页制作源码优化修复网站seo链接购买
  • 上海做网站最专业爱狼戈网站建设
  • 做网站公司昆山wordpress增强型短代码