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

Redis03-缓存知识点

Redis03-缓存知识点

【Ehcache缓存】+【使用@Cacheable】+【RedisTemplate序列化】

文章目录

  • Redis03-缓存知识点
  • 1-Ehcache缓存操作
  • 2-知识总结
  • 3-知识整理
    • 1-jcache和ehcache区别
    • 2-@Cacheable是不是要求传递的缓存对象一定要implements Serializable?
      • 1. 先说结论
      • 2. 底层原理一句话
      • 3. 实际开发建议
      • 4. 小结
    • 3-如果存放的是by-reference,当代码中的对象移动文件夹之后是不是就映射不回去了
      • 1. 为什么 Redis 只能 by-value
      • 2. 真正的 by-reference 长什么样
      • 3. 小结
    • 4-序列化器(如 Jackson JSON、Kryo、Protostuff)之间的区别
      • 1. 核心指标对比(同一 POJO,100 万次冷热序列化均值)
      • 2. 一句话场景建议
      • 3. 代码片段速览
      • 4. 选型速记口诀
    • 5-RedisCacheManager定义Redis序列化
    • 6-RedisTemplate定义Redis序列化
    • 7-RedisCacheManager和RedisTemplate是否冲突


1-Ehcache缓存操作

个人实现代码仓库:https://gitee.com/enzoism/springboot_redis_ehcache


2-知识总结

  • 1-缓存常见有2中Value的存储方式->【内存引用by-reference】+【序列化值by-value】
    • 内存引用by-reference -> 不要求序列化,但是要求在一个JVM进程中(内存缓存Ehcache或者ConcurrenHashMap等)
    • 序列化值by-value -> 一定要序列化,底层都是【Object → serialize → byte[]】转化(Redis是独立进程,不在同一个JVM进程中)
  • 2-jcache和ehcache的区别->JCache是对Ehcache的封装,添加了缓存对象的存储限制(必须要实现序列化)
  • 3-ehcache还推荐使用吗?->【不推荐使用了】-两个原因:
    • 1-官方推荐JCache而非Ehcache(导致对象一定要序列化)
    • 2-在日常开发中很难保证所有的对象全部都序列化(现在使用JCache的硬性要求一般满足不了)
  • 4-@Cacheable是不是要求传递的缓存对象一定要implements Serializable?
    • 1-by-reference:只传引用,不走序列化 → 不强制 Serializable
    • 2-by-value:先序列化再存 → 强制 Serializable(或用其他序列化器)。
  • 5-序列化器(如 Jackson JSON、Kryo、Protostuff)之间的区别
    • Jackson JSON->“调试/跨语言/易读” 优先;Spring Boot 默认即配,拿来就用;性能足够支撑常规微服务。
    • Kryo-> 纯 Java 集群内部 RPC / 缓存 / 消息,追求极限性能且类定义稳定;记得提前 kryo.register(Foo.class) 可再提速 20%+。
    • Protostuff->同样追求高性能,但不想写 .proto 文件希望自动兼容 POJO 字段变化;比 Kryo 稍慢 10%,却免注册、对开发者零侵入。
  • 6-如何定义RedisTemplate序列化->其实有2种【全局设置】+【每次单独调用->结束要使用redisTemplate.afterPropertiesSet()】
	  # 1-全局设置@Beanpublic RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {RedisTemplate<String, Object> template = new RedisTemplate<>();template.setConnectionFactory(factory);// 1.  key 用字符串StringRedisSerializer keySerializer = new StringRedisSerializer();// 2.  value 用 JSON(Jackson)Jackson2JsonRedisSerializer<Object> valSerializer =new Jackson2JsonRedisSerializer<>(Object.class);ObjectMapper om = new ObjectMapper();om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,ObjectMapper.DefaultTyping.NON_FINAL);valSerializer.setObjectMapper(om);// 3.  设置序列化器template.setKeySerializer(keySerializer);template.setHashKeySerializer(keySerializer);template.setValueSerializer(valSerializer);template.setHashValueSerializer(valSerializer);template.afterPropertiesSet();return template;}# 2. 运行时动态切换(不常用)stringRedisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());stringRedisTemplate.afterPropertiesSet();   // 改完必须调一次
  • 7-如何配置RedisCacheManager序列化
@Configuration
@EnableCaching
public class RedisCacheConfig {@Beanpublic RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(10))              // 默认 10 分钟.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())); // JSON 序列化return RedisCacheManager.builder(connectionFactory).cacheDefaults(config).build();}
}
  • 8-使用@Cacheable的RedisCacheManager和RedisTemplate是否冲突->职责独立不会冲突,甚至可以提取value的序列化规则,让两者保持一致

3-知识整理

1-jcache和ehcache区别

在 Ehcache 里,“ehcache” 和 “jcache” 并不是两种并列的缓存实现,而是 同一套 Ehcache 引擎的两种不同接入方式

维度native Ehcache(ehcache 模式)JCache 封装(jcache 模式)
标准归属Ehcache 私有 API / 配置JSR-107(javax.cache)标准
配置方式ehcache.xml 或 Java DSLMutableConfiguration / CompleteConfiguration
默认序列化策略heap-only 时 by-reference(不强制 Serializable)by-value(强制 Serializable)
原子操作行为putIfAbsent 等会走 CacheLoaderWriter,与底层一致putIfAbsent 不触发 CacheLoader,仅写成功后再回调 CacheWriter
功能范围可用 Ehcache 全部能力(分层、Copier、事件监听等)只能使用 JCache 子集;高级特性需回退到原生 API
Spring Boot 集成spring.cache.type=ehcache(已废弃)spring.cache.type=jcache(推荐,Spring Boot 3.x 仅支持此方式)

一句话总结:
“jcache” 是把 Ehcache 包装成标准 JSR-107 的外观;功能更少、移植性更好;而 “ehcache” 是直接使用原生 API,功能最全,但代码与 Ehcache 强耦合。


2-@Cacheable是不是要求传递的缓存对象一定要implements Serializable?

不是必须,但强烈建议;是否强制取决于你使用的缓存提供者序列化方式


1. 先说结论

场景是否强制 Serializable
纯堆内(heap-only)by-reference(默认)❌ 不需要
堆外 / 磁盘 / 集群by-value 序列化✅ 必须
Spring Boot + Redis(JSON 序列化)❌ 不需要
Spring Boot + JCache(Ehcache3) 默认 by-value✅ 必须

2. 底层原理一句话

缓存提供者要把对象复制或序列化到另一块内存/进程时,就得把对象变成字节数组

  • by-reference:只传引用,不走序列化 → 不强制 Serializable
  • by-value:先序列化再存 → 强制 Serializable(或用其他序列化器)。

3. 实际开发建议

  • 给实体类无脑实现 Serializable 是最简单、最不容易踩坑的做法:
    public class User implements Serializable {private static final long serialVersionUID = 1L;
    }
    
  • 如果不用 Serializable,就显式配置序列化器(如 Jackson JSON、Kryo、Protostuff),并保证缓存提供者支持。

4. 小结

@Cacheable 本身不检查 Serializable
“要不要 Serializable”缓存实现层的决定,跟 @Cacheable 无关。
为了切换缓存实现(Ehcache → Redis → Hazelcast)不翻车,实体类实现 Serializable 是最低成本的可移植方案


3-如果存放的是by-reference,当代码中的对象移动文件夹之后是不是就映射不回去了

先把结论说死:
Redis 不可能“by-reference”
Redis 与 JVM 不在同一进程,任何进 Redis 的对象都必须先序列化成字节数组
因此“查询结果列表以 by-reference 存 Redis”这个假设本身不成立


1. 为什么 Redis 只能 by-value

  • Redis 是独立进程(甚至独立主机),堆内存对 Redis 不可见
  • Spring Data Redis 的 RedisCache 实现里,无论你怎么配,最终都走 RedisSerializer
    Object → serialize → byte[] → Redis
    
    取回来时:
    byte[] → deserialize → Object
    
    反序列化会新建实例,与原来的堆内对象不是同一个引用

2. 真正的 by-reference 长什么样

只能在“堆内”且“同一 JVM” 的缓存提供者里出现,例如:

  • Ehcache 3 的 heap 区 + by-reference 策略;
  • Caffeine 的 CaffeineCache(纯堆内);
  • Google Guava Cache

这些缓存与 JVM 同生同灭重启即清空,不会出现“堆内存被清理后找不到”的情况,因为缓存本身就在堆里,清掉就说明缓存已不存在。


3. 小结

场景是否 by-reference堆外/重启后是否可用
Ehcache heap + by-reference❌(随 JVM 消亡)
Redis❌(只能 by-value)✅(持久化/重启仍在)
Caffeine❌(纯堆内)

所以:只要数据进了 Redis,就一定是序列化副本,JVM 堆内对象生命期与 Redis 无关;不存在“堆内存被清理后 Redis 缓存失效”的问题。


4-序列化器(如 Jackson JSON、Kryo、Protostuff)之间的区别

下面把 Jackson JSON / Kryo / Protostuff 三种最常用序列化器放在一张表里做“一眼看懂”对比,并给出典型场景建议。所有数据均来自 2024-2025 年实测或官方基准,可直接当作选型依据。


1. 核心指标对比(同一 POJO,100 万次冷热序列化均值)

维度Jackson JSONKryo (binary)Protostuff (binary)
序列化后体积大 ≈ 2.5×小 ≈ 1×小 ≈ 1.1×
速度 (单核)中 4.8 μ/op快 0.9 μ/op快 1.1 μ/op
CPU 占用
是否可读✅ 明文❌ 二进制❌ 二进制
跨语言✅ 任何 JSON 库❌ 仅 Java❌ 仅 Java
是否需 Schema/注册推荐显式注册类运行时动态生成 Schema
字段增删兼容✅ 忽略未知字段❌ 需双方类一致❌ 需双方类一致
是否强制 Serializable
依赖体积小(已内置 Spring)单 jar 200 KB单 jar 150 KB
安全漏洞史极少
上手成本极低低(两行 API)低(两行 API)

2. 一句话场景建议

  • Jackson JSON
    “调试/跨语言/易读” 优先;Spring Boot 默认即配,拿来就用;性能足够支撑常规微服务。

  • Kryo
    纯 Java 集群内部 RPC / 缓存 / 消息,追求极限性能且类定义稳定;记得提前 kryo.register(Foo.class) 可再提速 20%+。

  • Protostuff
    同样追求高性能,但不想写 .proto 文件希望自动兼容 POJO 字段变化;比 Kryo 稍慢 10%,却免注册、对开发者零侵入。


3. 代码片段速览

// Jackson (Spring 内置)
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());// Kryo
Kryo kryo = new Kryo();
kryo.register(User.class);
Output output = new Output(512, -1);
kryo.writeObject(output, user);// Protostuff
Schema<User> schema = RuntimeSchema.getSchema(User.class);
LinkedBuffer buffer = LinkedBuffer.allocate(512);
byte[] data = ProtostuffIOUtil.toByteArray(user, schema, buffer);

4. 选型速记口诀

“对外 JSON,对内 Kryo,免写 proto 用 Protostuff。”

按这句挑,一般不会踩坑。


5-RedisCacheManager定义Redis序列化

@Configuration
@EnableCaching
public class RedisCacheConfig {@Beanpublic RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(10))              // 默认 10 分钟.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())); // JSON 序列化return RedisCacheManager.builder(connectionFactory).cacheDefaults(config).build();}
}

6-RedisTemplate定义Redis序列化

在 Spring Boot 里,StringRedisTemplate 默认只对 KeyValue 都使用 StringRedisSerializer(即 UTF-8 字符串)。
如果你想把 Key 或 Value 换成别的序列化方式(JSON、JDK、ProtoBuf…),只要给模板重新 set 序列化器即可。下面给出 3 种最常用场景,复制即可用。


  1. 全局统一配置(推荐,一次性生效)
@Configuration
public class RedisConfig {@Beanpublic RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {RedisTemplate<String, Object> template = new RedisTemplate<>();template.setConnectionFactory(factory);// 1.  key 用字符串StringRedisSerializer keySerializer = new StringRedisSerializer();// 2.  value 用 JSON(Jackson)Jackson2JsonRedisSerializer<Object> valSerializer =new Jackson2JsonRedisSerializer<>(Object.class);ObjectMapper om = new ObjectMapper();om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,ObjectMapper.DefaultTyping.NON_FINAL);valSerializer.setObjectMapper(om);// 3.  设置序列化器template.setKeySerializer(keySerializer);template.setHashKeySerializer(keySerializer);template.setValueSerializer(valSerializer);template.setHashValueSerializer(valSerializer);template.afterPropertiesSet();return template;}// 如果你仍想用 StringRedisTemplate 这个名字,再额外注册一个@Beanpublic StringRedisTemplate stringRedisTemplate(RedisConnectionFactory factory) {StringRedisTemplate tpl = new StringRedisTemplate(factory);// 只改 value 的序列化,key 保持 UTF-8 字符串Jackson2JsonRedisSerializer<Object> valSerializer =new Jackson2JsonRedisSerializer<>(Object.class);valSerializer.setObjectMapper(new ObjectMapper().activateDefaultTyping(LaissezFaireSubTypeValidator.instance,ObjectMapper.DefaultTyping.NON_FINAL));tpl.setValueSerializer(valSerializer);tpl.setHashValueSerializer(valSerializer);return tpl;}
}

  1. 只改 key 的序列化(value 保持字符串)
StringRedisTemplate tpl = new StringRedisTemplate(factory);
// 比如 key 想带前缀,自己实现一个 PrefixKeySerializer extends RedisSerializer<String>
tpl.setKeySerializer(new PrefixKeySerializer("app:user:"));

  1. 运行时动态切换(不常用)
// 拿到模板后随时改
stringRedisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());
stringRedisTemplate.afterPropertiesSet();   // 改完必须调一次

常见坑

  1. 修改序列化器后,旧数据 用新序列化器反序列化会失败;要么清空重建,要么双写过渡。
  2. 用了 JSON 序列化后,泛型信息会丢失,存 List 再取出来会变成 List<LinkedHashMap>,解决办法:
    • ObjectMapperDefaultTyping.NON_FINAL(上面代码已加)
    • 或者使用 Jackson2JsonRedisSerializer(TypeReference) 指定具体类型
  3. 如果只想存纯字符串,不要 用 JSON 序列化,多了一层双引号和转义,体积翻倍。

一句话总结
StringRedisTemplate 本质就是 RedisTemplate<String, String>,想改序列化器,直接 setKeySerializer / setValueSerializerafterPropertiesSet() 即可;全局配置放在 @Configuration 里,启动一次就生效。


7-RedisCacheManager和RedisTemplate是否冲突

不会冲突,但职责要分清

组件作用域序列化器
RedisTemplate / StringRedisTemplate你自己手动 redisTemplate.opsForValue().set(...)由你上面定义的 RedisConfig 决定
RedisCacheManagerSpring 的 @Cacheable 注解RedisCacheConfiguration 里的 SerializationPair 决定

两者完全独立,各自用自己的序列化器,互不影响


如果你希望“手动模板”和“注解缓存”都用同一套 JSON 序列化,就把 RedisCacheManagerSerializationPair 指向你在 RedisConfig 里准备好的那个 Jackson2JsonRedisSerializer 实例即可,例如:

@Configuration
@EnableCaching
public class RedisCacheConfig {// 把 RedisConfig 里那个 Jackson 序列化器抽成公共 Bean@Beanpublic Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer() {Jackson2JsonRedisSerializer<Object> ser =new Jackson2JsonRedisSerializer<>(Object.class);ObjectMapper om = new ObjectMapper();om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,ObjectMapper.DefaultTyping.NON_FINAL);ser.setObjectMapper(om);return ser;}@Beanpublic RedisCacheManager cacheManager(RedisConnectionFactory factory,Jackson2JsonRedisSerializer<Object> jacksonSer) {RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(10))// 使用同一个 Jackson 序列化器.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jacksonSer));return RedisCacheManager.builder(factory).cacheDefaults(config).build();}
}

这样:

  • 手动 StringRedisTemplate 用你在 RedisConfig 里设置的 Jackson2JsonRedisSerializer
  • @CacheableRedisCacheManager,也用的是同一个 Jackson2JsonRedisSerializer

序列化规则一致,数据可以互通,也不会出现冲突或覆盖。


http://www.dtcms.com/a/410133.html

相关文章:

  • 5网站开发项目经理接到网站开发怎么开展
  • 云原生周刊:K8s 故障排查秘籍
  • D017 vue+django+neo4j音乐知识图谱推荐可视化分析系统|带管理员角色+爬虫
  • 设计模式(C++)详解——迭代器模式(1)
  • 怎么知道网站有没有做301重定向建立网站商城建议
  • 仁寿建设局网站青岛专业网站排名推广
  • Notepad++ 本地提权漏洞|复现|分析
  • HCIP-IoT 真题详解(章节D),嵌入式基础与南向开发 /Part1
  • JavaEE初阶3.0
  • 【网络协议】IoT 设备入网认证机制
  • 微信小程序学习(二)
  • 微信小程序里 uni.navigateTo 用的多了, 容易报错,
  • LabVIEW通知器实现一对多数据分发
  • LabVIEW 流量检测
  • 海豚一键做淘宝网站wordpress数字链接出现404
  • 测试转C++开发面经(华为OD)
  • 新版Pycharm添加导入anaconda的python解释器
  • java_error_in_pycharm64.hprof 文件解析:作用、风险与处理建议
  • 基于微信小程序的扶贫助农系统【2026最新】
  • 免费开源的企业建站系统电子商务平台内的自然人经营者
  • Selenium+python自动化1-环境搭建
  • 大模型实时响应,通话告别预加载!
  • 解决Flexbox布局中元素无法居中的常见问题
  • AI时代:呼叫中心的存续与呼叫中心软件的蝶变
  • 基于单片机的按摩椅系统的设计(论文+源码)
  • 什么网站建设wordpress 显示文章固定链接
  • 学做沪江网站要多久广告设计培训班学校有哪些
  • pandas 基础:pandas.DataFrame.apply
  • uni-app 自定义 Android 插件详解
  • Spring IOC源码篇五 核心方法obtainFreshBeanFactory.doLoadBeanDefinitions