用豆包辅助从头开始学习java spring cloud(七)
用豆包辅助从头开始学习java spring cloud七昨天学习了数据库事务以及一些基本概念今天学习redis基本内容。Redis 是开源的内存型 Key‑Value 数据库是内存数据库主要用来做缓存减轻 DB 压力解决高性能、分布式业务场景优势是速度快、数据结构多受内存限制要注意大 key 和数据丢失问题。具体理论大家自行搜索下面进入今天的学习内容Day5 Redis基础实操 前提本地启动Redis 1. SpringBoot项目引入spring‑boot‑starter‑data‑redis 2. yml配置redis连接RedisTemplate简单使用字符串读写。 3. 概念理解缓存穿透、缓存击穿、缓存雪崩不需要手写完整解决方案理解概念每种问题对应的解决思路即可。之前安装数据库的时候安装了docker所以这次直接拉一个redis镜像回来直接使用docker pull redis:7-alpine docker run -d --name redis-local -p 6379:6379 redis:7-alpine第一部分、代码准备1、引入 Maven 依赖pom.xml 添加 redis 启动器之后刷新 Maven。!-- redis --dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-redis/artifactId/dependency2、application.yml 配置 Redisspring:redis:host:127.0.0.1port:6379password:database:0###database:0使用第 0 号库默认。3、写测试 Controller操作 RedisTemplatepackagecom.single.bootsingledemo.controller;importorg.springframework.data.redis.core.RedisTemplate;importorg.springframework.web.bind.annotation.GetMapping;importorg.springframework.web.bind.annotation.RequestMapping;importorg.springframework.web.bind.annotation.RestController;importjavax.annotation.Resource;importjava.util.concurrent.TimeUnit;RestControllerRequestMapping(/redis)publicclassRedisTestController{ResourceprivateRedisTemplateString,ObjectredisTemplate;/* * 设置字符串缓存 * */GetMapping(/set)publicStringsetCache(){//key,value,过期时间单位秒redisTemplate.opsForValue().set(user:name,张三,60,TimeUnit.SECONDS);return写入redis成功;}/* * 获取缓存 * */GetMapping(/get)publicObjectgetCache(){returnredisTemplate.opsForValue().get(user,name);}/* * 删除缓存 * */GetMapping(/del)publicStringdelCache(){redisTemplate.delete(user:name);return删除成功;}}4、测试分别访问http://127.0.0.1:8080/redis/set写入/get查询/del删除,每一步测试时在redis客户端查看key的变化1.写入​ a.写入之前​ b.调接口写入启动项目前注意一下昨天写得代码其中AccountServiceImpl.java中的AutowiredprivateAccountServiceself;testInnerCall()方法 还有inti1/0删除掉或者注释掉AccountService中的testInnerCall()方法也需要去掉AccountController中的innerCall()部分也去掉启动后直接浏览器访问写入接口查看redis客户端可以观察60秒后是不是会失效2.查询3.删除以上就是对redis的增删查。下面开始准备链接数据库。第二部分、给【根据 id 查询用户】接口增加 Redis 缓存业务逻辑根据 id 查询用户优先查 Redis 缓存Redis 有数据 → 直接返回不走 MySQLRedis 没有 → 查询 MySQL 数据库MySQL 查到数据 → 把用户对象存入 Redis设置过期时间再返回给前端MySQL 查不到直接返回 null后面可以处理缓存穿透1修改UserService.javapackagecom.single.bootsingledemo.service;importcom.baomidou.mybatisplus.extension.service.IService;importcom.single.bootsingledemo.entity.User;publicinterfaceUserServiceextendsIServiceUser{UsergetUserById(Longid);}2.修改UserServiceImpl.java改造getById查询方法packagecom.single.bootsingledemo.service.impl;importcom.baomidou.mybatisplus.extension.service.impl.ServiceImpl;importcom.single.bootsingledemo.entity.User;importcom.single.bootsingledemo.mapper.UserMapper;importcom.single.bootsingledemo.service.UserService;importorg.springframework.data.redis.core.RedisTemplate;importorg.springframework.stereotype.Service;importjavax.annotation.Resource;importjava.util.concurrent.TimeUnit;ServicepublicclassUserServiceImplextendsServiceImplUserMapper,UserimplementsUserService{ResourceprivateRedisTemplateString,ObjectredisTemplate;/* * 根据id查询用户带redis缓存 * */publicUsergetUserById(Longid){//1.组装redis key规范userid值Stringkeyuser:id;//先查redisUsercacheUser(User)redisTemplate.opsForValue().get(key);if(cacheUser!null){//缓存命中直接返回不访问数据库returncacheUser;}//缓存没命中查询mysqlUserdbUserbaseMapper.selectById(id);//数据库查到写入redis设置120秒过期if(dbUser!null){redisTemplate.opsForValue().set(key,dbUser,120,TimeUnit.SECONDS);}returndbUser;}}3.修改 UserController,增加一个接口GetMapping(/cache/{id})publicUsergetCacheUser(PathVariableLongid){returnuserService.getUserById(id);}4.重启项目测试新接口本地地址/user/cache/1第一次访问接口缓存没有执行 MySQL 查询控制台打印 mybatis 的 sql 日志同时把数据存入 Redis刷新浏览器第二次访问直接读取 Redis控制台看不到 SQL 语句缓存命中不走数据库。于是尝试访问后发现不太对也执行了查询但是没有正常返回但是正常查询接口是正常的那么这个问题就是通过redis的时候产生的问题也就是RedisTemplate 默认使用 JDK 序列化要求存入 Redis 的实体类User必须实现 Serializable序列化接口现在 User 实体没有实现保存对象到 Redis 直接序列化失败 500 报错。解决办法有两种方法一在User类上面直接加上实现Serializable就正常了。packagecom.single.bootsingledemo.entity;importcom.baomidou.mybatisplus.annotation.IdType;importcom.baomidou.mybatisplus.annotation.TableId;importcom.baomidou.mybatisplus.annotation.TableName;importlombok.Data;importjava.io.Serializable;DataTableName(user)publicclassUserimplementsSerializable{//增加implements SerializableTableId(typeIdType.AUTO)privateLongid;privateStringname;privateIntegerage;privateStringemail;}重启后正常访问但是有一个隐患可能会有缓存穿透问题这个问题我们随后再说先说第二个方法方法二不改动User类新建配置类com.single.bootsingledemo.config.RedisConfig.javapackagecom.single.bootsingledemo.config;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.data.redis.connection.RedisConnectionFactory;importorg.springframework.data.redis.core.RedisTemplate;importorg.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;importorg.springframework.data.redis.serializer.StringRedisSerializer;ConfigurationpublicclassRedisConfig{BeanpublicRedisTemplateString,ObjectredisTemplate(RedisConnectionFactoryfactory){RedisTemplateString,ObjecttemplatenewRedisTemplate();template.setConnectionFactory(factory);//key使用字符串序列化StringRedisSerializerstringRedisSerializernewStringRedisSerializer();template.setKeySerializer(stringRedisSerializer);template.setHashKeySerializer(stringRedisSerializer);//value 使用jackson json序列化实体类不需要实现SerializableJackson2JsonRedisSerializerObjectjsonRedisSerializernewJackson2JsonRedisSerializer(Object.class);template.setValueSerializer(jsonRedisSerializer);template.setHashValueSerializer(jsonRedisSerializer);template.afterPropertiesSet();returntemplate;}}使用这个配置的好处Redis 里面存的是可读 JSON 字符串User 实体不需要 implements Serializable。直接用这个配置就解决序列化报错企业项目基本都是这种 JSON 序列化方案。建议直接采用这个 RedisConfig 配置类不用修改 User 实体一步解决序列化报错。第一次访问第二次访问额。。。。又遇到问题了现在是第一次请求查数据库把 User 存入 RedisJackson 序列化变成 JSON✅第二次请求从 Redis 读出 JSONJackson 把它解析成 LinkedHashMap不会自动转成 User 实体直接强转 User 就抛类型转换异常。redisTemplate.opsForValue().get(key)返回的是 ObjectJSON 反序列化出来是 Map不是 User 对象不能直接强转 User。现在需要对UserServiceImpl.java进行修改packagecom.single.bootsingledemo.service.impl;importcom.baomidou.mybatisplus.extension.service.impl.ServiceImpl;importcom.fasterxml.jackson.databind.ObjectMapper;importcom.single.bootsingledemo.entity.User;importcom.single.bootsingledemo.mapper.UserMapper;importcom.single.bootsingledemo.service.UserService;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.data.redis.core.RedisTemplate;importorg.springframework.stereotype.Service;importjavax.annotation.Resource;importjava.util.concurrent.TimeUnit;ServicepublicclassUserServiceImplextendsServiceImplUserMapper,UserimplementsUserService{ResourceprivateRedisTemplateString,ObjectredisTemplate;AutowiredprivateObjectMapperobjectMapper;/* * 根据id查询用户带redis缓存 * */publicUsergetUserById(Longid){//1.组装redis key规范userid值Stringkeyuser:id;//先查redisObjectcacheObjredisTemplate.opsForValue().get(key);// User cacheUser (User) redisTemplate.opsForValue().get(key);if(cacheObj!null){//缓存命中直接返回不访问数据库returnobjectMapper.convertValue(cacheObj,User.class);}//缓存没命中查询mysqlUserdbUserbaseMapper.selectById(id);//数据库查到写入redis设置120秒过期if(dbUser!null){redisTemplate.opsForValue().set(key,dbUser,120,TimeUnit.SECONDS);}else{redisTemplate.opsForValue().set(key,null,30,TimeUnit.SECONDS);}returndbUser;}}现在重启项目进行第一次访问不再赘述第二次访问redis有缓存所以第二次没有再次访问数据库。5.缓存穿透这时访问一个不存在的id如/user/cache/8多次查询依然每次都在查询数据库但是redis实际已经缓存了我们再次修改代码packagecom.single.bootsingledemo.service.impl;importcom.baomidou.mybatisplus.extension.service.impl.ServiceImpl;importcom.fasterxml.jackson.databind.ObjectMapper;importcom.single.bootsingledemo.entity.User;importcom.single.bootsingledemo.mapper.UserMapper;importcom.single.bootsingledemo.service.UserService;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.data.redis.core.RedisTemplate;importorg.springframework.stereotype.Service;importjavax.annotation.Resource;importjava.util.concurrent.TimeUnit;ServicepublicclassUserServiceImplextendsServiceImplUserMapper,UserimplementsUserService{ResourceprivateRedisTemplateString,ObjectredisTemplate;AutowiredprivateObjectMapperobjectMapper;/* * 根据id查询用户带redis缓存 * */publicUsergetUserById(Longid){//1.组装redis key规范userid值Stringkeyuser:id;//先查redisObjectcacheObjredisTemplate.opsForValue().get(key);// User cacheUser (User) redisTemplate.opsForValue().get(key);if(cacheObj!null){//缓存命中直接返回不访问数据库returnobjectMapper.convertValue(cacheObj,User.class);}//缓存没命中查询mysqlUserdbUserbaseMapper.selectById(id);//数据库查到写入redis设置120秒过期if(dbUser!null){redisTemplate.opsForValue().set(key,dbUser,120,TimeUnit.SECONDS);}else{redisTemplate.opsForValue().set(key,,30,TimeUnit.SECONDS);//把null改为了一个空值}returndbUser;}}改完重启第二次访问时报错了空字符串我们加入一个判断OverridepublicUsergetUserById(Longid){Stringkeyuser:id;ObjectcacheObjredisTemplate.opsForValue().get(key);if(cacheObj!null){// 是空字符串标记代表数据库不存在这条数据直接返回null不要去做对象转换if(.equals(cacheObj)){returnnull;}// 正常的用户JSON才做转换returnobjectMapper.convertValue(cacheObj,User.class);}UserdbUserbaseMapper.selectById(id);if(dbUser!null){redisTemplate.opsForValue().set(key,dbUser,120,TimeUnit.SECONDS);}else{//存入空字符串占位30秒过期解决缓存穿透redisTemplate.opsForValue().set(key,,30,TimeUnit.SECONDS);}returndbUser;}修改之后 对id8进行多次访问不在多次查询数据库页面正常返回且redis有缓存。多次访问均为同一返回值没再报其他错误。后台也没再多次查询数据库6.一些知识点缓存穿透查不存在的数据缓存查不到全部打数据库。方案缓存空值、布隆过滤器。缓存击穿热点 key 过期大量并发打同一个 key 到数据库。方案分布式锁、逻辑永不过期。缓存雪崩大量 key 同一时刻过期流量压垮数据库。方案过期时间加随机值、Redis 集群、限流降级。我们在上面演示了缓存穿透但是缓存击穿和雪崩无法演示代码在后期做实战项目时再进行尝试现在停留在理解概念的位置。第三部分小结一、环境使用 Docker 启动 Redis 服务安装 Redis 可视化客户端连接 Redis方便查看 key、TTL、value我这里安装的是Another-Redis-Desktop-Manager-win-1.7.2-x64SpringBoot 单体项目引入spring‑boot‑starter‑data‑redis依赖yml 配置 Redis 连接信息使用RedisTemplate操作 Redis。二、代码实操通过用户查询增加缓存逻辑实际业务为通过id查询用户优先读取 Redis 缓存缓存命中直接返回缓存数据不访问 MySQL缓存未命中查询 MySQL数据库查到数据存入 Redis设置过期时间返回数据数据库查不到数据不能存nullJackson 序列化会直接丢弃 key缓存失效存入空字符串 “” 作为空标记设置较短过期时间解决缓存穿透。踩坑点读到空字符串标记时不能交给 ObjectMapper 做实体转换直接 return null否则抛出类型转换异常。三、三大redis缓存问题1. 缓存穿透现象查询数据库不存在的数据缓存一直不命中请求全部打到数据库。解决缓存空标记本次代码实现、布隆过滤器、参数合法性校验2. 缓存击穿现象单个热点 key 过期瞬间大量并发请求过来缓存失效全部打数据库。解决分布式互斥锁、热点 key 逻辑永不过期、主动提前刷新缓存。3. 缓存雪崩现象大批量 key 同一时刻过期大量流量压垮数据库。解决过期时间增加随机偏移、Redis 集群高可用、服务限流降级。区分记忆穿透数据本身不存在击穿单个热点 key 失效雪崩大批 key 集体失效。四、额外知识点更新业务优先更新数据库删除缓存不直接更新缓存复杂场景使用延时双删降低数据不一致时间窗口。五、明日学习计划Day6 架构理论 RESTful规范2h不需要敲大量代码重点思考和笔记。架构演进单体架构的痛点 → 垂直拆分 → SOA → 微服务微服务优缺点服务拆分原则按业务域、单一职责。RESTful API规范GET查询、POST新增、PUT修改、DELETE删除路径命名规范。思考练习题如果做一个电商系统如何拆分为微服务哪些拆成独立服务写下你的拆分思路。2. 缓存击穿现象单个热点 key 过期瞬间大量并发请求过来缓存失效全部打数据库。解决分布式互斥锁、热点 key 逻辑永不过期、主动提前刷新缓存。3. 缓存雪崩现象大批量 key 同一时刻过期大量流量压垮数据库。解决过期时间增加随机偏移、Redis 集群高可用、服务限流降级。区分记忆穿透数据本身不存在击穿单个热点 key 失效雪崩大批 key 集体失效。四、额外知识点更新业务优先更新数据库删除缓存不直接更新缓存复杂场景使用延时双删降低数据不一致时间窗口。五、明日学习计划Day6 架构理论 RESTful规范2h不需要敲大量代码重点思考和笔记。架构演进单体架构的痛点 → 垂直拆分 → SOA → 微服务微服务优缺点服务拆分原则按业务域、单一职责。RESTful API规范GET查询、POST新增、PUT修改、DELETE删除路径命名规范。思考练习题如果做一个电商系统如何拆分为微服务哪些拆成独立服务写下你的拆分思路。

相关新闻

明明接口幂等就能防重复提交,为什么还要加分布式锁?

明明接口幂等就能防重复提交,为什么还要加分布式锁?

这个问题挺有代表性的,我在做 Code Review 的时候也见过不少人把这两个东西搞混,觉得做了幂等就万事大吉了,分布式锁是多余的。幂等和分布式锁解决的根本不是同一个问题。幂等管的是结果正确性,分布式锁管的是并发执行顺序。两者是…

2026/10/25 11:03:23 阅读更多 →
2026年个人远程控制软件盘点:哪款才是你的居家办公最佳搭档?

2026年个人远程控制软件盘点:哪款才是你的居家办公最佳搭档?

远程控制早已不是企业IT运维的专属工具。下班路上临时调取家中电脑的文件、出差时远程操作办公室的台式机、帮远在老家的父母解决手机卡顿——这些日常场景让远程控制软件走进了每个人的生活。但市面上的远控工具五花八门,有的连上就卡顿掉线,有的免费版…

2026/10/25 15:32:06 阅读更多 →
从循环工程到原生模型切换:Codex与Claude Code的Agent调优实践

从循环工程到原生模型切换:Codex与Claude Code的Agent调优实践

如果你最近频繁使用 Codex 或 Claude Code,大概率撞到过一个“看起来很努力、实际上原地打转”的 Agent 场景:它反复修改同一个文件,测试在红绿之间来回跳,日志刷了一批又一批,但问题始终没有收敛。很多人第一反应是“…

2026/11/1 10:32:09 阅读更多 →

最新新闻

Electron桌面应用开发实战:从环境搭建到打包发布全流程

Electron桌面应用开发实战:从环境搭建到打包发布全流程

做前端这些年,我大部分时间都在跟浏览器标签页打交道,直到第一次把桌面应用跑起来,才发现原来自己一直被限制在地址栏和刷新按钮围起来的框框里。Electron这个词,最开始是在某段时间频繁出现在同事的讨论里,一个用HTML…

2026/11/1 0:24:43 阅读更多 →
滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

简介:亚太天能T109滑盖智能锁产品说明书是一份PDF格式的官方技术文档,主要面向智能锁安装人员、终端用户及售后维护者,帮助快速掌握产品安装、配置与常见故障处理。全包仅1个PDF文件,大小4.37MB,内容结构清晰&#xff…

2026/11/1 10:36:53 阅读更多 →
PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

上个月我在调试一台高速AGV的驱动系统,整车要求空载跑到8m/s,电机转速已经拉到额定点,驱动器的输出电压也顶到了母线电压极限附近。再往上踩速度指令,转速纹丝不动,三相电流反而开始震荡。那会儿我的第一反应就是&…

2026/11/1 10:36:51 阅读更多 →
C++核心知识点:从内存管理到STL的实战指南

C++核心知识点:从内存管理到STL的实战指南

C 这门语言我前前后后折腾了十几年,用它写过嵌入式驱动、做过游戏服务端,也搭过基础组件库。每次看到新人一头扎进"C 核心知识点"这个搜索词,我其实挺感慨的——这门语言的难点从来不是语法多,而是知识点之间的耦合太深…

2026/11/1 1:06:58 阅读更多 →
T109滑盖智能锁怎么装?安装调试与故障排查全攻略

T109滑盖智能锁怎么装?安装调试与故障排查全攻略

简介:亚太天能T109滑盖智能锁产品说明书以PDF格式发布,适合智能锁用户、安装人员与售后维修人员阅读。内容围绕T109系列指纹密码刷卡锁展开,先介绍前面板、后面板、锁体、机械钥匙、电池盒盖等部件名称与功能,再给出完整安装步骤&…

2026/11/1 10:35:11 阅读更多 →
LLM知识降熵:从信息垄断到理解权下放

LLM知识降熵:从信息垄断到理解权下放

你有没有过这种经历:翻开源码注释、论文摘要或者一份专业报告,每个字都认识,连在一起却像天书。想找人聊聊,身边要么没人懂,要么懂的懒得跟你解释。后来我试着把同一段内容丢给一个大语言模型,几秒钟之后&a…

2026/11/1 9:35:41 阅读更多 →

日新闻

周新闻

月新闻