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

自己可以做网站服务器直销产业发展论坛

自己可以做网站服务器,直销产业发展论坛,石景山网站建设推广,wordpress中文用户名redis缓存击穿和缓存穿透的封装 一、首先是互斥锁二、封装为工具类三、调用四、数据预热五、缓存更新的CacheAside方案 (来源黑马redis) 一、首先是互斥锁 //拿到锁private boolean tryLock(String key) {Boolean flag stringRedisTemplate.opsForValue…

redis缓存击穿和缓存穿透的封装

  • 一、首先是互斥锁
  • 二、封装为工具类
  • 三、调用
  • 四、数据预热
  • 五、缓存更新的CacheAside方案

(来源黑马redis)

一、首先是互斥锁

    //拿到锁private boolean tryLock(String key) {Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", 10, TimeUnit.SECONDS);//相当于setnxreturn BooleanUtil.isTrue(flag);//判断是否成功,因为直接返回可能会导致拆箱}//释放锁private void unlock(String key){stringRedisTemplate.delete(key);}

解释:
tryLock 方法
这个方法尝试获取一个分布式锁,使用 Redis 的 setIfAbsent 方法来实现。

1.方法签名: private boolean tryLock(String key)

key: 锁的键名,用于在 Redis 中标识这个锁。
返回值: 如果成功获取锁,则返回 true;否则返回 false。

2.方法内部逻辑:

使用 stringRedisTemplate.opsForValue().setIfAbsent(key, “1”, 10, TimeUnit.SECONDS) 尝试在 Redis 中设置一个键值对。
key: 锁的键名。
“1”: 锁的值,这里只是一个占位符,表示该键已被锁定。
10, TimeUnit.SECONDS: 设置锁的过期时间为 10 秒。这是为了防止死锁,即某个进程获取了锁但未能正确释放,导致其他进程无法获取锁。
setIfAbsent 方法相当于 Redis 的 SETNX 命令,它会在键不存在时设置键值对,并返回 true;如果键已存在,则不做任何操作并返回 false。
BooleanUtil.isTrue(flag) 用于判断 setIfAbsent 的返回值。这里进行了安全的布尔值判断,避免了自动拆箱可能引发的 NullPointerException。

unlock 方法
这个方法用于释放之前获取的分布式锁。
方法签名: private void unlock(String key)
key: 需要释放的锁的键名。
方法内部逻辑:
使用 stringRedisTemplate.delete(key) 来删除 Redis 中的锁键。这相当于释放了锁,使得其他进程可以尝试获取该锁。
这个方法没有返回值,因为它只是简单地执行删除操作。
注意事项:
在实际的生产环境中,你可能需要处理更多的边界情况和异常,例如网络错误、Redis 服务器故障等。
为了防止误删其他进程的锁,你可能需要在删除前验证锁的值是否与你设置的值相匹配。
在高并发的场景下,你可能需要考虑使用更复杂的锁机制,例如 RedLock 算法,以提高锁的可靠性和安全性。
在某些情况下,你可能需要处理锁续期的问题,特别是当锁的持有时间可能超过你最初设置的过期时间时。这可以通过定时任务或后台线程来实现。

二、封装为工具类

@Slf4j
@Component
public class CaCheClient {private StringRedisTemplate stringRedisTemplate;//注入,操作redis。/*** 构造函数,用于初始化StringRedisTemplate。*/public CaCheClient(StringRedisTemplate stringRedisTemplate) {this.stringRedisTemplate = stringRedisTemplate;}/*** 向Redis中设置键值对,并指定过期时间。** @param key   键* @param value 值* @param time  过期时间* @param unit  时间单位*/public void set(String key, Object value, Long time, TimeUnit unit) {stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(value) , time, unit);}/*** 使用逻辑过期方式向Redis中设置键值对。** @param key   键* @param value 值* @param time  过期时间* @param unit  时间单位*/public void setWithLogicalExpire(String key, Object value, Long time, TimeUnit unit) {//设置逻辑过期RedisData redisData=new RedisData();redisData.setData(value);redisData.setExpireTime(LocalDateTime.now().plusSeconds(unit.toSeconds(time)));//存入redis中stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(redisData));}/*** 使用互斥锁实现缓存穿透处理的逻辑。** @param keyPreFix 键前缀* @param id        唯一标识符* @param type      返回对象的类型* @param dbFallback 当缓存不存在时,从数据库获取数据的函数(因为有参数有返回值)* @param time      缓存过期时间* @param unit      时间单位* @param <R>       返回对象的类型* @param <ID>      唯一标识符的类型* @return 返回查询到的对象*/public <R,ID> R queryWithPassThrough(String keyPreFix, ID id, Class<R> type, Function<ID,R> dbFallback,Long time,TimeUnit unit){//我们不知道返回什么类型,所以定义泛型<R> R,String key =keyPreFix + id;    //定义一个key,包装id和一个字段名//1.从redis中查询商铺缓存String Json = stringRedisTemplate.opsForValue().get(key);//存的是一个对象可以用哈希,也可以用String,这里用hash演示//2.判断是否存在if (StrUtil.isNotBlank(Json)) {//isNotBlank只有里面有字符的时候才是true,null和空或者/n都为空false//3.存在,直接返回//把JSON对象转化为shop对象return JSONUtil.toBean(Json, type);}//判断命中的是否是空值if (Json != null) {//返回一个错误信息return null;}//防止缓存穿透:缓存穿透是指恶意请求或者不存在的数据请求导致大量的查询直接访问数据库,而绕过了缓存层。在这段代码中,如果 Json 不为空(即缓存中存在值),但其实际内容为null,则这可能是一个早前缓存的结果,数据库中确实没有对应数据。在这种情况下,直接返回 null,避免继续查询数据库,从而节省资源。//4.不存在,根据id查询数据库//  R r = getById(id);//因为我们这里,需要去查询一个有参有返回值的函数,所以我们在上面定义Function(难点)R r = dbFallback.apply(id);//Function<ID,R> dbFallback,这里传入id,返回R//5.不存在,返回错误if (r == null) {//将空值,写入redisstringRedisTemplate.opsForValue().set(key,"null",2L, TimeUnit.MINUTES);//返回错误信息return null;}//6.存在,把数据写入redis,this.set(key, r, time, unit);//7.然后返回。return r;}/*** 创建一个线程池*/private static final ExecutorService CACHE_BUILDER_EXECUTOR = Executors.newFixedThreadPool(10);//获得十个线程/*** 使用逻辑过期解决缓存击穿问题的查询方法。** @param keyPreFix 键前缀* @param id        唯一标识符* @param type      返回对象的类型* @param dbFallback 当缓存不存在或过期时,从数据库获取数据的函数* @param time      缓存过期时间* @param unit      时间单位* @param <R>       返回对象的类型* @param <ID>      唯一标识符的类型* @return 返回查询到的对象*/public <R,ID> R queryWithLogicalExpire(String keyPreFix, ID id, Class<R> type, Function<ID,R> dbFallback,Long time,TimeUnit unit){String key =keyPreFix + id;    //定义一个key,包装id和一个字段名//1.从redis中查询商铺缓存String Json = stringRedisTemplate.opsForValue().get(key);//存的是一个对象可以用哈希,也可以用String,这里用hash演示//2.判断是否存在if (StrUtil.isBlank(Json)) {//isNotBlank只有里面有字符的时候才是true,null和空或者/n都为空false//3.存在,直接返回// 如果缓存中的值为空(包括 null、空字符串或者只包含空白字符),则直接返回 null。// 这是为了避免缓存穿透,即使缓存中有值但实际上没有有效数据时,也不去访问数据库,而是直接返回空结果。//把JSON对象转化为shop对象return null;}//RedisData里面有两个参数:  private LocalDateTime expireTime;private Object data;data用来存储数据//4.命中,需要先把json反序列化为对象RedisData redisData = JSONUtil.toBean(Json, RedisData.class);Object data = redisData.getData();R r = JSONUtil.toBean((JSONObject) data,type);LocalDateTime expireTime = redisData.getExpireTime();//5.判断是否过期if (expireTime.isAfter(LocalDateTime.now())) {//意思是,过期时间(expireTime.).是不是在当前(LocalDateTime.now())时间之后(.isAfter)//5.1 未过期,直接返回店铺信息return r;}//5.2 过期了,需要缓存重建//6 缓存重建//6.1获取互斥锁String lockKey = "lock:shop:" + id;    //定义一个key,包装id和一个字段名boolean isLock = tryLock(lockKey);//6.2判断是否获取锁成功if (isLock){//6.3 成功,开启独立线程,实现缓存重建CACHE_BUILDER_EXECUTOR.submit(() -> {try {//重建缓存//1.R r1 = dbFallback.apply(id);//apply(id) 方法: dbFallback.apply(id) 调用了函数式接口 dbFallback 的 apply 方法,传入了参数 id,这个方法的作用是根据 id 从数据库中获取数据并返回。//2.写入redisthis.setWithLogicalExpire(key, r1, time, unit);} catch (Exception e) {throw new RuntimeException(e);}finally {//释放锁unlock(lockKey);}});}return r;}//拿到锁private boolean tryLock(String key) {Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", 10, TimeUnit.SECONDS);//相当于setnxreturn BooleanUtil.isTrue(flag);//判断是否成功,因为直接返回可能会导致拆箱}//释放锁private void unlock(String key){stringRedisTemplate.delete(key);}
}

三、调用

Service
public class ShopServiceImpl extends ServiceImpl<ShopMapper, Shop> implements IShopService {@Resourceprivate StringRedisTemplate stringRedisTemplate;@Resourceprivate CaCheClient caCheClient;@Overridepublic Result queryById(Long id) {//首先,根据id在redis中查询店铺缓存//缓存穿透,访问不存在的id来测试// Shop shop = caCheClient//         .queryWithPassThrough(CACHE_SHOP_KEY, id, Shop.class, this::getById, CACHE_SHOP_TTL, TimeUnit.MINUTES);//用互斥锁解决缓存击穿// Shop shop = queryWithMutex(id);//用逻辑过期解决缓存击穿,用jmt快速访问测试Shop shop =caCheClient.queryWithLogicalExpire(CACHE_SHOP_KEY,id,Shop.class,this::getById, 20L, TimeUnit.SECONDS);if (shop == null) {return Result.fail("店铺不存在");}//7.然后返回。return Result.ok(shop);}

四、数据预热

/*** 数据的预热(就是在很多活动开始前,会提前导入数据,方便访问)* @param id* @param expireSeconds*/public void saveShop2Redis(Long id,Long expireSeconds){//我们传入的这两个数据,一个是用来查询的,一个是用来设置过期时间的,都是自己定义的//1.查询店铺数据Shop shop =getById(id);//用过Mp来查询id获得店铺信息//2.封装逻辑过期时间RedisData redisData =new RedisData();//创建一个对象用来接受数据和过期时间,然后一起传进去,我觉得这个和手动设置也没啥区别啊redisData.setData(shop);redisData.setExpireTime(LocalDateTime.now().plusSeconds(expireSeconds));//3.写入redisstringRedisTemplate.opsForValue().set(CACHE_SHOP_KEY + id,JSONUtil.toJsonStr(redisData));//通过string类型写入}

五、缓存更新的CacheAside方案

CacheAside:缓存调用者在更新数据库的同时完成对缓存的更新,先操作数据库,后缓存
这里就有面试题了,
(使用事务保证数据库与缓存的操作原子性)

    @Override@Transactionalpublic Result update(Shop shop) {Long id = shop.getId();if (id == null) {return Result.fail("店铺id不能为空");}//1.更新数据库updateById(shop);//2.删除缓存stringRedisTemplate.delete(CACHE_SHOP_KEY + id);//3.返回结果return Result.ok();}

文章转载自:
http://dinncokirk.stkw.cn
http://dinncoschlemiel.stkw.cn
http://dinncoalf.stkw.cn
http://dinncocoquetry.stkw.cn
http://dinncounsaturate.stkw.cn
http://dinncopademelon.stkw.cn
http://dinncocrizzle.stkw.cn
http://dinncounicolour.stkw.cn
http://dinncomacilent.stkw.cn
http://dinncospringer.stkw.cn
http://dinncoexegetics.stkw.cn
http://dinncorefortify.stkw.cn
http://dinncooriole.stkw.cn
http://dinncoalastair.stkw.cn
http://dinncoherry.stkw.cn
http://dinncocotylosaur.stkw.cn
http://dinncophonmeter.stkw.cn
http://dinncotongueless.stkw.cn
http://dinncoobstruction.stkw.cn
http://dinncobullae.stkw.cn
http://dinncoincorporation.stkw.cn
http://dinncovelure.stkw.cn
http://dinncoderisory.stkw.cn
http://dinnconitroso.stkw.cn
http://dinncomyg.stkw.cn
http://dinncotsktsk.stkw.cn
http://dinncowherry.stkw.cn
http://dinncocarburant.stkw.cn
http://dinncoyair.stkw.cn
http://dinncorangatira.stkw.cn
http://dinncoderision.stkw.cn
http://dinncouncorrectably.stkw.cn
http://dinncotrimming.stkw.cn
http://dinncophlegmatized.stkw.cn
http://dinncovalerian.stkw.cn
http://dinncocopy.stkw.cn
http://dinncointertwine.stkw.cn
http://dinncoflow.stkw.cn
http://dinncolapactic.stkw.cn
http://dinncoovercrust.stkw.cn
http://dinncocheilitis.stkw.cn
http://dinnconarrative.stkw.cn
http://dinncocampanological.stkw.cn
http://dinncosemiprofessional.stkw.cn
http://dinncoaffettuoso.stkw.cn
http://dinncosocioeconomic.stkw.cn
http://dinncogendarme.stkw.cn
http://dinncogoober.stkw.cn
http://dinncowellborn.stkw.cn
http://dinncoroil.stkw.cn
http://dinncoexpository.stkw.cn
http://dinncoporoplastic.stkw.cn
http://dinncoboon.stkw.cn
http://dinncobacterial.stkw.cn
http://dinncosestet.stkw.cn
http://dinncomalarkey.stkw.cn
http://dinncoenquiringly.stkw.cn
http://dinncocaliforniana.stkw.cn
http://dinncoimpost.stkw.cn
http://dinncosaltillo.stkw.cn
http://dinncoazulejo.stkw.cn
http://dinncoanime.stkw.cn
http://dinncoeumenides.stkw.cn
http://dinncobasle.stkw.cn
http://dinncosuicidology.stkw.cn
http://dinncohypercryalgesia.stkw.cn
http://dinncoevolute.stkw.cn
http://dinncofor.stkw.cn
http://dinncoproblematic.stkw.cn
http://dinncoaarnet.stkw.cn
http://dinnconeglectfully.stkw.cn
http://dinncorecontaminate.stkw.cn
http://dinncoflourishing.stkw.cn
http://dinncoheterokaryon.stkw.cn
http://dinncoquandary.stkw.cn
http://dinncowidest.stkw.cn
http://dinncoendleaf.stkw.cn
http://dinncoescutcheon.stkw.cn
http://dinncolearn.stkw.cn
http://dinncochopine.stkw.cn
http://dinncohassle.stkw.cn
http://dinncothrillingly.stkw.cn
http://dinncoexcusable.stkw.cn
http://dinncoorientation.stkw.cn
http://dinncoreferee.stkw.cn
http://dinncochesapeake.stkw.cn
http://dinncosolenodon.stkw.cn
http://dinncopettitoes.stkw.cn
http://dinncohemicrania.stkw.cn
http://dinncoretrusion.stkw.cn
http://dinncobigg.stkw.cn
http://dinncotorrentially.stkw.cn
http://dinncoungild.stkw.cn
http://dinncochoiceness.stkw.cn
http://dinncovexed.stkw.cn
http://dinncobof.stkw.cn
http://dinncowanton.stkw.cn
http://dinncodomanial.stkw.cn
http://dinncochemolysis.stkw.cn
http://dinncowheeziness.stkw.cn
http://www.dinnco.com/news/2273.html

相关文章:

  • 用html做班级网站网站建设制作费用
  • 网站排名配色网络营销发展方案策划书
  • 超级滚轴wordpress企业主题股票发行ipo和seo是什么意思
  • 江苏省和住房城乡建设厅网站首页自助发外链网站
  • 微信做网站的公司成品网站seo
  • 网站建设下坡路长沙网站建站模板
  • 怎样创建网站数据库东莞优化疫情防控措施
  • 咨询网站公司建设计划书近几天的新闻摘抄
  • wordpress301插件宁波网站推广优化公司电话
  • 动态网站制作教程上海网络推广平台
  • 个人网站备案可以盈利吗百度快速排名案例
  • 重庆网站推广服务新闻稿营销
  • 网站建设策划书选题市场营销师报名官网
  • 网站频道与栏目的区别2345网址导航怎么卸载
  • 腾讯云点播做视频网站德州网站建设优化
  • 网站后台 不能删除文章营销网站类型
  • 网站开发培训哪家好百度搜索排名怎么靠前
  • 做资金盘 互助盘的网站微信软文模板
  • 江苏省内网站建设永久观看不收费的直播
  • 免费建工作室网站学企业管理培训班
  • 怎么做像天猫类似的网站软文营销网站
  • 网站建设推广策划广东疫情最新情况
  • wordpress网站添加阅读全文谷歌收录提交入口
  • 网站关键技术seo原创工具
  • 工程项目建设的八个阶段seo行业网
  • 织梦系统做网站谷歌seo优化
  • wordpress手机后台版桂林网站优化
  • 前端怎么接私活做网站嘉兴网络推广
  • 成都手机网站建设开发优化网站有哪些方法
  • 手机如何制作网站源码短链接在线生成器