Redis缓存注解怎么用,研究一下它的具体应用和注意点
- 问答
- 2025-12-31 03:03:05
- 3
Redis缓存注解,主要是在Java的Spring框架里用的一个省时省力的工具,它就像是个小助手,你告诉它哪些方法的结果需要记下来(缓存),下次再问同样的问题时,它就能直接把记下的答案告诉你,省得方法再辛辛苦苦地跑一遍了,这特别适合用在那些查询频繁、但又不会经常变动的数据上,比如商品信息、用户的基本资料、网站配置啥的。
几个核心的注解怎么用
这几个注解都来自Spring框架的缓存抽象模块,用之前得先在项目里配置好Redis的连接和缓存管理器。
-
@Cacheable:最常用的“记下来”注解
- 干什么用:这个注解贴在方法上,意思是“这个方法的结果值得缓存”,当方法第一次被调用时,它会正常执行,执行完后,Spring会把返回值放进Redis里,之后只要再用完全相同的参数调用这个方法,Spring就会直接去Redis里拿结果,方法本体根本不会执行。
- 怎么用:
@Cacheable(value = "users", key = "#userId") public User findUserById(Long userId) { // 这里是模拟去数据库查数据的耗时操作 System.out.println("正在查询数据库,用户ID: " + userId); return userRepository.findById(userId); } - 解释一下:
value = "users":指定缓存结果要放在哪个“仓库”里,在Redis里会变成一个叫users的键前缀。key = "#userId":这是缓存的“精确门牌号”,这里用了Spring的表达式(SpEL),#userId就是指方法的参数userId,这样,查询用户1的结果和查询用户2的结果会被分别缓存,不会搞混,如果不指定key,Spring会使用所有参数组合成一个默认key。
- 应用场景:绝大多数根据ID查询单个对象的场景,比如查用户、查商品详情。
-
@CacheEvict:负责“清理缓存”的注解
- 干什么用:当数据发生变化时(比如修改、删除),旧缓存就失效了,必须清掉,否则下次读到的就是过时的脏数据。
@CacheEvict就是干这个的。 - 怎么用:
@CacheEvict(value = "users", key = "#userId") public void updateUser(Long userId, User user) { // 先更新数据库 userRepository.update(user); // 方法执行成功后,注解会自动清除掉 key 为 #userId 的缓存 } - 注意点:它有个重要的属性叫
allEntries,如果设置为true,就会清空整个value对应的“仓库”(比如清空所有users开头的缓存),慎用!一般只在执行“清理所有缓存”的操作时才用。
- 干什么用:当数据发生变化时(比如修改、删除),旧缓存就失效了,必须清掉,否则下次读到的就是过时的脏数据。
-
@CachePut:“不管有没有,都更新一下缓存”
- 干什么用:这个注解贴的方法,每次都会执行,不会去查缓存,它执行完后,会把结果更新到缓存里,常用于新增或更新操作后,希望最新的结果立刻被缓存起来。
- 怎么用:
@CachePut(value = "users", key = "#result.id") public User createUser(User user) { // 创建新用户 User savedUser = userRepository.save(user); // 方法返回后,返回值(savedUser)的id会作为key,整个savedUser对象作为value,被放入缓存 return savedUser; } - 和@Cacheable的区别:
@Cacheable是“有缓存就用,不执行方法”;@CachePut是“方法必须执行,并且用结果刷新缓存”。
实际应用中要留神的几个点
-
缓存穿透问题
- 是什么:黑客可能会故意请求一个数据库中根本不存在的数据(比如id=-1的用户),因为数据库没有,所以缓存里也不会有,每次请求都会穿过缓存打到数据库上,给数据库造成巨大压力。
- 怎么办:
- 缓存空值:即使数据库查不到,也在缓存里存一个空值(比如
null)并设置一个较短的过期时间,这样下次同样的请求过来,缓存就能拦住。 - 参数校验:在接口层做好校验,把明显不合法的参数(如负数ID)直接挡回去。
- 使用布隆过滤器:这是一个更高级的解决方案,可以高效地判断一个数据是否“绝对不存在”于数据库中。
- 缓存空值:即使数据库查不到,也在缓存里存一个空值(比如
-
缓存雪崩问题
- 是什么:指大量缓存数据在同一时间过期失效,或者Redis服务本身宕机,导致所有请求瞬间都涌向数据库,数据库承受不住就可能挂掉。
- 怎么办:
- 设置不同的过期时间:不要给所有缓存设置相同的过期时间,可以在基础过期时间上,加上一个随机的偏移量(比如30分钟 ± 5分钟),让缓存失效的时间点分散开。
- Redis高可用:搭建Redis集群(主从复制、哨兵模式),避免单点故障。
- 服务降级:在应用层面,如果发现缓存失效且数据库压力大,可以对非核心业务直接返回降级信息(如“服务繁忙”),保护核心业务和数据库。
-
缓存和数据库的数据一致性问题
- 是什么:这是最头疼的问题,比如你先更新了数据库,然后要清除缓存,但如果清除缓存的操作失败了,或者因为网络延迟导致清除缓存的请求晚于另一个读请求,那么读请求就会把旧的脏数据又读出来放进缓存。
- 怎么办:这是一个复杂话题,没有银弹。
- 简单策略:尽量让写操作顺序是“先更新数据库,再删除缓存”,这种策略在大多数情况下是可行的,但理论上仍存在极小的不一致窗口期。
- 复杂策略:对于一致性要求极高的场景(如资金账户),可能需要引入更复杂的机制,比如基于数据库binlog的同步(阿里巴巴的Canal组件)、或者使用分布式事务(代价高),但这已经超出了简单注解的范畴。
-
序列化问题
- 是什么:Java对象要存到Redis里,需要转换成字节(序列化);从Redis里读出来,需要变回对象(反序列化),如果序列化方式没配置好,或者对象结构变了(比如增删了字段),就可能读不出来数据或者报错。
- 怎么办:在Spring配置中,选择稳定可靠的序列化器,比如Jackson2JsonRedisSerializer或GenericJackson2JsonRedisSerializer,对于缓存的对象,要尽量避免频繁改变其类结构。
总结一下
Redis缓存注解用起来非常方便,几行代码就能给应用加上一层高速缓存,但要想用好它,不能只是简单地贴上 @Cacheable 就完事了,你必须心里有数,知道可能会遇到缓存穿透、雪崩、数据不一致这些“坑”,并提前想好应对策略,它是一把提升性能的利器,但也需要小心使用,否则可能会带来意想不到的麻烦。
(主要参考思路来源于Spring官方文档关于缓存抽象的章节、以及国内技术社区如CSDN、博客园上大量关于缓存经典问题的讨论和解决方案。)

本文由寇乐童于2025-12-31发表在笙亿网络策划,如有疑问,请联系我们。
本文链接:http://waw.haoid.cn/wenda/71635.html
