redistemplate需显式配置genericjackson2jsonredisserializer等序列化器,避免默认jdk序列化问题;操作时须设过期时间、判空防穿透、用原子命令、正确删除缓存。

RedisTemplate 的基础配置和序列化问题
不配好序列化器,RedisTemplate 写进去的数据根本读不出来,或者反序列化失败报 SerializationException。默认用的是 JdkSerializationRedisSerializer,存的是二进制字节,人眼不可读,且 Java 类变更后极易反序列化失败。
实际项目中建议统一用 StringRedisTemplate 处理字符串类缓存(如 token、计数器),或为 RedisTemplate 显式设置 GenericJackson2JsonRedisSerializer(推荐)或 StringRedisSerializer:
redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer()); redisTemplate.setHashKeySerializer(new StringRedisSerializer()); redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
注意:必须在 afterPropertiesSet() 之前完成设置,否则会被重置;如果 key 是对象,记得重写 equals() 和 hashCode(),否则 opsForHash().put() 可能覆盖错槽位。
用 opsForValue() 做字符串缓存的读写
这是最常用场景,比如缓存用户信息 JSON 字符串、配置项、临时 token。别直接用 redisTemplate.opsForValue().set("user:1001", user) 就完事——没过期时间会永久占内存,没异常兜底可能缓存穿透。
-
set(key, value, timeout, TimeUnit.SECONDS)必须带过期时间,常见值是 5–30 分钟 - 读取时用
opsForValue().get(key),返回Object,需强制转型;若用StringRedisTemplate,则返回String,更安全 - 写入前建议判空:
if (user != null) redisTemplate.opsForValue().set(...),避免缓存 null 值导致穿透 - 批量操作用
opsForValue().multiGet(keys),比循环 get 效率高,但注意返回 List 中可能含null
用 opsForHash() 缓存对象字段级数据
适合缓存一个对象的多个属性,比如用户昵称、头像 URL、积分,又不想每次全量序列化整个对象。这时别用 opsForValue() 存整个 map,而要用 opsForHash() 拆开存。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
例如缓存用户基础字段:
HashOperations<string string object> hashOps = redisTemplate.opsForHash();
hashOps.put("user:1001", "nickname", "张三");
hashOps.put("user:1001", "avatar", "https://xxx.jpg");
hashOps.put("user:1001", "score", 1200);</string>
读单个字段:hashOps.get("user:1001", "score");读全部字段:hashOps.entries("user:1001")。注意:key 是 hash 表名(如 user:1001),field 是字段名(如 "nickname"),二者类型都要和序列化器匹配——如果 field 是对象,也得确保它可被 GenericJackson2JsonRedisSerializer 序列化。
缓存失效与原子性操作的坑
expire() 和 set() 不是原子的,中间若进程崩溃,key 就没过期时间;getAndSet() 虽能原子读写,但会覆盖旧值,不适合“有则更新、无则跳过”逻辑。
- 设过期时间务必和写入合并:用
opsForValue().set(key, value, timeout, unit),而不是分开调set()+expire() - 实现“先查缓存,未命中再查 DB 并回写”的逻辑时,记得加锁(如 Redis 分布式锁),否则高并发下会击穿 DB
-
opsForValue().increment()是原子的,适合计数器;但increment()对非数字值会抛RedisSystemException,写入前确保 key 对应 value 是数字字符串或 Long - 删除缓存用
delete(key),不是set(key, null)——后者会把 null 写进 Redis,后续 get 返回非空但内容为 null,语义混乱
真正难的从来不是“怎么写”,而是“什么时候删”“删不掉怎么办”“删早了还是删晚了”。这些边界条件比 API 调用本身更消耗调试时间。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










