网站建设陷阱怎样更新网站

张小明 2026/1/7 13:21:36
网站建设陷阱,怎样更新网站,免费自助在线公司起名,设计公司有哪些部门《Redis-day03-商户查询缓存》 0. 今日总结 了解了什么是缓存完成了利用redis实现根据id查询商铺及商铺类型缓存解决了缓存更新导致的数据不一致问题#xff0c;解决方案如下#xff1a; 读操作#xff1a;缓存未命中则查询数据库#xff0c;并将结果写入缓存#xff0c;但…《Redis-day03-商户查询缓存》0. 今日总结了解了什么是缓存完成了利用redis实现根据id查询商铺及商铺类型缓存解决了缓存更新导致的数据不一致问题解决方案如下读操作缓存未命中则查询数据库并将结果写入缓存但是额外设置一个超时时间。写操作修改数据时先修改数据库中的数据再删除缓存。通过缓存空对象解决了缓存穿透问题分别实现了通过互斥锁和逻辑过期解决缓存击穿问题通过泛型、函数式接口、方法引用将穿透、击穿等的解决方法抽象为了工具类1. 什么是缓存缓存就是数据交换的缓冲区(称作Cache[kæJ])是存贮数据的临时地方一般读写性能较高。2. 添加Redis缓存2.1 缓存模型2.2 根据id查询商铺controllerservice先创建key作为redis的key操作String类型的redis从redis中根据key查找是否存在对应的缓存如果存在则将缓存转化为shop类型的对象然后返回给前端如果不存在则通过mybatis-plus从数据库中根据id查找对应的商铺如果数据库中不存在则返回店铺不存在如果数据库中存在则将数据写入redis中注意要将shop重新转化为Json类型才能写入将数据库中查找到的shop对象返回给前端2.3 店铺类型缓存controllerservice从redis中查找是否存在缓存如果存在调用JSONUtil的toList方法将s转化为List集合并返回给前端如果不存在则查询数据库用mybatis-plust的query方法及.list方法获得查询结果this指代当前对象继承自ServiceImpl这里的泛型参数ShopTypeMapper, ShopType明确告知框架该服务层操作的是ShopType实体并使用ShopTypeMapper进行数据访问如果结果为空说明列表不存在如果不为空将列表存入缓存并将结果返回给前端3. 缓存更新策略3.1 实现一致性的三种方法如果不实现缓存和数据库的一致性可能会导致从缓存中查到的数据和数据库中实际存储的数据不一致的情况3.2 主动更新策略3.3 删除缓存和更新数据库的顺序先删除缓存再操作数据库情况1情况2先操作数据库再删除缓存情况1情况2发生可能性较低3.4 缓存更新最佳方案3.5 代码实现超时时间设置修改店铺时先修改数据库再删除缓存3.6 功能测试用POSTMAN修改茶餐厅name为101茶餐厅redis中数据被成功删除数据库数据正常修改刷新后用户界面正常显示101茶餐厅redis中重新写入店铺缓存4. 缓存穿透4.1 概念和解决方案缓存穿透产生原因缓存穿透是指客户端请求的数据在缓存中和数据库中都不存在这样缓存永远不会生效这些请求都会打到数据库。如果有恶意攻击发起大量的并发的空线程疯狂查询不存在的缓存可能会造成数据库崩溃缓存穿透的两种解决方案缓存空对象不管查询结果存不存在都将结果缓存到Redis这样发起同样的请求查找redis时就能获得一个结果而不用去查询数据库**优点**实现简单维护方便缺点可能造成额外的内存消耗可能造成短期的不一致例如用户请求了一个id这个id是null但是此时真的给该id添加了一条数据到数据库那么就出现了数据库缓存不一致布隆过滤**优点**内存占用少没有多余key缺点实现复杂存在误判可能4.2 缓存空对象的实现查询到结果时判断是否为空值StrUtil.isNotBlank只能判断是否存在实际值如果shopJson是空值也会返回false因此这里要再判断一次shopJson不为null也就是确实查到了shopJson值为shopJson “”数据库没查到时将空值写入缓存4.3 功能测试第一次查询不存在的店铺服务器会对数据库发起查询查询结束后会将数据缓存到redis中第二次查询不会再走数据库5. 缓存雪崩5.1 概念和解决方案缓存雪崩是指在同一时段大量的缓存key同时失效或者Redis服务宕机导致大量请求到达数据库带来巨大压力。解决方案给不同的Key的TTL添加随机值利用Redis集群提高服务的可用性给缓存业务添加降级限流策略给业务添加多级缓存6. 缓存击穿6.1 概念和解决方案缓存击穿问题也叫热点Key问题就是一个被高并发访问并且缓存重建业务较复杂的key突然失效了无数的请求访问会在瞬间给数据库带来巨大的冲击。解决方案互斥锁思路如果查找失败去查找数据库前加一个锁防止别的线程查找数据库逻辑过期思路提前缓存热点key并且永远不清除而是添加一个逻辑过期时间因此如果查找失败则说明确实不存在该数据直接返回空即可。如果查找成功都判断一下是否过期没过期说明查找确实成功。过期了则尝试获取锁去查找数据库并重置过期时间。如果获取锁失败说明别的线程已经在这么做了那就先不管将旧的数据直接返回两种方案对比6.2 互斥锁的实现概括互斥锁其实就相当于在缓存穿透的基础上如果未命中缓存即将要操作数据库前增加一个加锁过程保证只要有线程发起了对数据库的请求就保证在其操作数据库结束前其他的线程在发送相同请求同样未命中缓存即将操作数据库时拦截掉该数据库操作6.2.1 代码实现按以下流程配合redis的setnx key value仅在key不存在时为key设置value操作实现获取锁和释放锁逻辑利用redis String的setnx命令如果设置成功说明当前锁为空返回ture否则返回失败说明当前key被上锁删除对应key这样再对该key进行setnx就能正常赋值就能视为获取锁了缓存击穿逻辑基于缓存穿透/** * 缓存击穿穿透 * * param id * return */publicShopqueryWithMutex(Longid){StringkeyRedisConstants.CACHE_SHOP_KEYid;//1.从redis里查询商铺缓存StringshopJsonstringRedisTemplate.opsForValue().get(key);//2.判断是否存在if(StrUtil.isNotBlank(shopJson)){//3.存在直接返回returnJSONUtil.toBean(shopJson,Shop.class);}//判断命中的是否是空值//StrUtil.isNotBlank只能判断是否存在实际值如果shopJson是空值也会返回false因此这里要再判断一次if(shopJson!null){// 返回错误信息returnnull;}//4.实现缓存重建//4.1 获取互斥锁StringlockKeyRedisConstants.LOCK_SHOP_KEYid;Shopshopnull;try{//调用tryLock方法如果设置成功说明获取锁成功否则说明锁被占用尚未释放booleanisLocktryLock(lockKey);//4.2 判断是否获取成功if(!isLock){//4.3 获取锁失败休眠并重试Thread.sleep(50);//递归调用returnqueryWithMutex(id);}//4.4 获取锁成功//再次判断redis缓存是否存在因为有可能在等待获取锁的过程中别人已经将缓存写入了StringshopJson2stringRedisTemplate.opsForValue().get(key);if(StrUtil.isNotBlank(shopJson2)){//4.4.1 存在直接返回returnJSONUtil.toBean(shopJson2,Shop.class);}//4.4.2 不存在查询数据库shopgetById(id);//5.数据库不存在返回错误if(shopnull){//将空值写入redisstringRedisTemplate.opsForValue().set(key,,RedisConstants.CACHE_NULL_TTL,TimeUnit.MINUTES);returnnull;}//6.数据库存在写入redisstringRedisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(shop),RedisConstants.CACHE_SHOP_TTL,TimeUnit.MINUTES);}catch(InterruptedExceptione){thrownewRuntimeException(e);}finally{//7.释放互斥锁unlock(lockKey);}//8.返回returnshop;}从redis查数据如果查询成功返回数据如果查询失败判断是否为空值如果为空值返回错误信息缓存穿透解决方案如果不为空值而是确实没查到进入下一步获取锁占用资源对数据库进行查询并将结果写入缓存获取互斥锁用setnx写入keylockKey的数据如果写入失败说明当前key已经被占用且尚未删除说明其他线程占用了锁此时等待50毫秒然后递归调用该方法再次查询如果写入成功说明获取锁成功再次判断redis缓存是否存在因为有可能在等待获取锁的过程中别人已经将缓存写入了如果存在直接返回如果不存在查询数据库查询数据库数据库不存在将空值写入redis防止缓存穿透数据库存在将值写入redis释放互斥锁6.2.2 功能测试高并发场景下数据库制备查找了一次6.3 逻辑过期的实现6.3.1 代码实现问题原本的shop对象中没有维护过期时间字段作为逻辑过期时间解决方案在Shop中增加一个字段要修改Shop不方便创建一个RedisData类维护过期时间字段再让Shop继承RedisData这个类依旧要修改Shop类不方便创建一个RedisData类维护过期时间字段和Object data字段data字段专门存储数据可以是shop对象也可以是其他对象基于逻辑过期查找的实现/** * 基于逻辑过期查找 * * param id * return */publicShopqueryWithLogicalExpire(Longid){StringkeyRedisConstants.CACHE_SHOP_KEYid;//1.从redis里查询商铺缓存StringshopJsonstringRedisTemplate.opsForValue().get(key);//2.判断是否命中if(StrUtil.isBlank(shopJson)){//3.未命中返回空returnnull;}//4.命中把json反序列化为对象RedisDataredisDataJSONUtil.toBean(shopJson,RedisData.class);JSONObjectdata(JSONObject)redisData.getData();ShopshopJSONUtil.toBean(data,Shop.class);LocalDateTimeexpireTimeredisData.getExpireTime();//简单写法Shop shop JSONUtil.toBean((JSONObject) redisData.getData(), Shop.class);//5.判断是否过期if(expireTime.isAfter(LocalDateTime.now())){//5.1未过期直接返回returnshop;}//5.2已过期需要缓存重建//6.缓存重建//6.1获取互斥锁StringlockKeyRedisConstants.LOCK_SHOP_KEYid;booleanisLocktryLock(lockKey);//6.2判断是否成功if(isLock){//6.2.1成功再次检测redis缓存是否过期StringshopJson2stringRedisTemplate.opsForValue().get(key);if(StrUtil.isNotBlank(shopJson2)){// 如果未过期直接返回JSONUtil.toBean(shopJson2,Shop.class);}// 如果过期则开启独立线程实现缓存重建CACHE_REBUILD_EXECUTOR.submit(()-{try{//重建this.saveShop2Redis(id,20L);}catch(Exceptione){thrownewRuntimeException(e);}finally{//释放锁unlock(lockKey);}});}//6.2.2失败返回过期的商铺信息//7.返回returnshop;}首先从redis里查询店铺缓存如果未命中直接返回空如果命中则反序列化获取Json中的RedisData对象反序列化获取RedisData对象维护了过期时间和Shop数据通过redisData获取维护的ExpireTime过期时间通过redisData获取维护的data店铺数据但是这个店铺数据由于在写入时被转化为了JSON对象而其本身时Object对象因此要将redisData.getData()转化为JSONObject才能正确的取出其中的JSON对象然后再调用JSONUtil.toBean将JSON对象转化为Shop对象判断是否逻辑过期未过期直接返回shop过期则需要重建缓存重建缓存获取互斥锁获取成功为了避免在获取锁的过程中别的线程已经将redis成功重建获取锁成功后要重新查询一次redis并判断是否过期如果此时变成未过期了说明别的线程已经重建缓存则直接返回如果已过期说明别的线程未重建缓存当前线程为第一个获取锁成功的线程开始重建缓存获取失败线程池会安排一个空闲的线程来执行它而调用submit的主线程处理用户请求的线程不会等待这个任务完成会立即继续向下执行。将热点数据写入redis并设置逻辑过期时间//数据预热将热点数据写入redis并设置逻辑过期时间publicvoidsaveShop2Redis(Longid,LongexpireSeconds)throwsInterruptedException{//1.查询店铺数据ShopshopgetById(id);//2.封装逻辑过期时间RedisDataredisDatanewRedisData();redisData.setData(shop);redisData.setExpireTime(LocalDateTime.now().plusSeconds(expireSeconds));//3.写入Redis没有添加实际过期时间stringRedisTemplate.opsForValue().set(RedisConstants.CACHE_SHOP_KEYid,JSONUtil.toJsonStr(redisData));}根据id查询店铺数据封装逻辑过期时间和店铺数据将封装后的RedisData对象写入redis7. 缓存工具封装7.1 将java对象保存到redis并设置过期时间由于是工具类因此不知道要封装的对象是什么类因此封装的对象是Object类保存到redis时用JSONUtil的toJsonStr方法直接将对象转换即可。过期时间和时间单位也由调用者传入7.2 将java对象保存到redis并设置逻辑过期时间由于是工具类因此不知道要封装的对象是什么类又由于是逻辑过期因此创建RedisData对象并为对象设置data和expireTime两个字段的值7.3 用缓存空值方式解决缓存穿透问题重要函数头R,ID表示函数中出现的R和ID为泛型String keyPrefix表示函数的第一个参数为redis中key的前缀R表示返回值因为返回值类型不确定因此用泛型指代ID表示idid类型也不确定也用泛型指代ClassR函数第三个参数type为调用者传入的类型该类型应该和返回值保持一致FunctionID, R dbFallbcak表示第四个参数为一个函数式接口该函数式接口调用apply方法时参数为ID返回值为R剩下两个参数表示过期时间和时间单位函数体设置key keyPrefix idkey为要查询的数据在redis中的key调用stringRedisTemplate.opsForValue().get(key)如果存在命中且不为空则直接返回返回类型为type也就是R类的Class如果命中且为空则返回null表示查找失败如果未命中则根据id查询数据库调用dbFallback的apply方法将ID类型的对象id作为参数并返回R类型的对象用r接收如果数据库不存在也就是r为空则将空值写入redis并返回null表示查找失败如果数据库存在则调用set方法将数据库写入redis并将数据返回调用工具类的queryWithPassThrough方法并传入以下参数queryWithPassThrough(RedisConstants.CACHE_SHOP_KEY, id, Shop.class, this::getById, RedisConstants.CACHE_SHOP_TTL, TimeUnit.MINUTES)RedisConstants.CACHE_SHOP_KEY表示传入的redis中的key的前缀id表示传入的idShop.class表示返回的数据类型时Shop类型对应到工具类中表示type为Shop.Classthis::getById这是方法引用相当于(Long)-(Shop)表示当前对象调用getById方法返回值为getById的返回值类型Shop参数为getById的参数类型Long。对应到工具类中表示函数式接口dbFallback被绑定为Shop getById(Long)因此在工具类中ID为getById的参数类型LongR为getById的返回值类型Shop并且在工具类中调用该回调方法R r dbFallback.apply(id);时id就是Long类型返回值就是Shop类型7.4 用逻辑过期问题方式解决缓存击穿问题思路和7.3完全类似publicR,IDRqueryWithLogicalExpire(StringkeyPrefix,StringlockKeyPrefix,IDid,ClassRtype,FunctionID,RdbFallback,Longtime){StringkeykeyPrefixid;//1.从redis里查询商铺缓存StringjsonstringRedisTemplate.opsForValue().get(key);//2.判断是否命中if(StrUtil.isBlank(json)){//3.未命中返回空returnnull;}//4.命中把json反序列化为对象RedisDataredisDataJSONUtil.toBean(json,RedisData.class);JSONObjectdata(JSONObject)redisData.getData();RrJSONUtil.toBean(data,type);LocalDateTimeexpireTimeredisData.getExpireTime();//简单写法Shop shop JSONUtil.toBean((JSONObject) redisData.getData(), Shop.class);//5.判断是否过期if(expireTime.isAfter(LocalDateTime.now())){//5.1未过期直接返回returnr;}//5.2已过期需要缓存重建//6.缓存重建//6.1获取互斥锁StringlockKeylockKeyPrefixid;booleanisLocktryLock(lockKey);//6.2判断是否成功if(isLock){//6.2.1成功再次检测redis缓存是否过期Stringjson2stringRedisTemplate.opsForValue().get(key);RedisDataredisData2JSONUtil.toBean(json2,RedisData.class);LocalDateTimeexpireTime2redisData2.getExpireTime();JSONObjectdata2(JSONObject)redisData2.getData();if(expireTime2.isAfter(LocalDateTime.now())){// 如果未过期直接返回returnJSONUtil.toBean(data2,type);}// 如果过期则开启独立线程实现缓存重建CACHE_REBUILD_EXECUTOR.submit(()-{try{//重建this.save2Redis(keyPrefix,id,time,dbFallback);}catch(Exceptione){thrownewRuntimeException(e);}finally{//释放锁unlock(lockKey);}});}//6.2.2失败返回过期的商铺信息//7.返回returnr;}/** * 尝试获得锁 * * param key * return */privatebooleantryLock(Stringkey){//利用redis String的setnx命令如果设置成功说明当前锁为空返回ture否则返回失败说明当前key被上锁BooleanflagstringRedisTemplate.opsForValue().setIfAbsent(key,1,10,TimeUnit.SECONDS);returnBooleanUtil.isTrue(flag);}/** * 尝试释放锁 * * param key */privatevoidunlock(Stringkey){//删除对应key这样再对该key进行setnx就能正常赋值就能视为获取锁了stringRedisTemplate.delete(key);}//数据预热将热点数据写入redis并设置逻辑过期时间publicR,IDvoidsave2Redis(StringkeyPrefix,IDid,LongexpireSeconds,FunctionID,RdbFallback)throwsInterruptedException{//1.查询店铺数据RrdbFallback.apply(id);//2.封装逻辑过期时间RedisDataredisDatanewRedisData();redisData.setData(r);redisData.setExpireTime(LocalDateTime.now().plusSeconds(expireSeconds));//3.写入Redis没有添加实际过期时间stringRedisTemplate.opsForValue().set(keyPrefixid,JSONUtil.toJsonStr(redisData));}7.5 泛型、函数式接口、方法引用复习7.5.1 泛型7.5.2 函数式接口核心概念只有一个抽象方法的接口因为只有一个抽象方法如果将函数式接口作为方法参数则该方法的调用者可以通过方法引用来简化Lambda表达式来对唯一的抽象方法进行重写函数式接口的真正威力在于它允许我们将行为而不仅仅是数据作为参数传递。7.5.3 方法引用方法引用把已经有的方法拿过来用当作函数式接口中抽象方法的方法体。
版权声明:本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

一号网站建设wordpress查询次数

Qwen3-8B-AWQ性能优化与多语言应用实践 在当前大模型部署成本高企的背景下,如何在有限硬件资源下实现高质量推理,成为开发者关注的核心问题。尤其是在消费级GPU环境中,既要保证生成质量,又要控制显存占用和响应延迟,这…

张小明 2026/1/5 22:01:45 网站建设

吴中区网站设计公司成都网站建设哪家好文章

APK Editor Studio 终极指南:一站式安卓应用编辑解决方案 【免费下载链接】apk-editor-studio Powerful yet easy to use APK editor for PC and Mac. 项目地址: https://gitcode.com/gh_mirrors/ap/apk-editor-studio 在移动应用开发日益普及的今天&#xf…

张小明 2026/1/5 22:01:47 网站建设

有了网站源码怎么做appwordpress文库

作者:打码养家 日期:2025年12月19日 场景:第三方登录系统重构(钉钉、企业微信等)一、背景:为什么需要重构?在开发一个 SaaS 平台时,我们最初采用最朴素的方式实现第三方登录&#xf…

张小明 2026/1/5 22:01:46 网站建设

建设学校网站的报告济南城市建设集团

AI与人类创作的对比 随着GPT-5.2的发布,人工智能在创作领域的表现让人惊叹不已。从写作到设计,再到音乐和艺术创作,GPT-5.2的能力不断扩展,令人不禁思考:人工智能的创造力,真的能够超越人类的想象力吗&…

张小明 2026/1/5 22:01:49 网站建设

营销型网站欣赏通用搭建网站教程

网络算法:标签传播、Kruskal 算法与加权网络模型 标签传播算法 标签传播算法是一种直观且高效的网络社区发现方法。其核心假设是:若图按社区组织,节点 $i$ 所属社区为 $C(i)$,那么 $i$ 的大多数邻居大概率也属于 $C(i)$。基于此,初始为各节点分配不同标签,随后反复更新…

张小明 2026/1/5 22:01:48 网站建设

郑州网站推广公司电话wordpress分类文章获取

第一章:你真的会用AutoGLM吗? AutoGLM 作为新一代自动化生成语言模型工具,集成了提示工程优化、上下文感知推理与多轮对话管理能力,但许多开发者仍停留在基础调用层面,未能充分发挥其潜力。真正掌握 AutoGLM 意味着理解…

张小明 2026/1/5 22:01:51 网站建设