redis五大数据类型与java无直接类型对应,需按操作意图匹配:string依存值类型选string/byte[];hash用stringredistemplate或配置json序列化;list勿当arraylist用;set/zset注意序列化导致的类型擦除问题。

Redis String 类型对应 Java 的哪些用法?
Redis 的 String 看似简单,但 Java 里没一个“直接等价物”——它既不是 String,也不是 byte[],而是取决于你存什么、怎么读。
常见错误是:用 Jedis.set("key", "value") 存字符串,再用 Jedis.get("key") 拿回来,结果在分布式锁或计数场景下出问题。
- 如果存的是纯文本(如用户昵称),
String没问题,但要注意编码:Jedis 默认用 UTF-8,Spring Data Redis 默认也是,但若用RedisTemplate且没配StringRedisSerializer,可能返回null或乱码 - 如果存的是序列化对象(比如
new User("a", 25)),别手写JSON.toJSONString()后塞进去——这会让后续 Lua 脚本无法解析,也丧失原子性操作能力 - 计数类场景(如限流器)优先用
INCR/DECR命令,而不是先GET再SET,否则并发下会丢数据
示例:正确做分布式计数
jedis.incr("rate_limit:uid_123"); // 原子自增,不用管类型Redis Hash 怎么映射成 Java 对象才不翻车?
HASH 是最常被拿来模拟“Java 对象”的结构,但很多人一上来就用 HashOperations.putAll() 批量塞整个 Map<string object></string>,结果发现字段丢了、类型变了、甚至空指针。
-
Object值必须可序列化,但 Spring Data Redis 的默认JdkSerializationRedisSerializer会把LocalDateTime序列成一堆字节,读出来是java.time.Ser实例,反序列化失败 - 更稳妥的做法是:所有字段转成字符串(
LocalDateTime.now().toString())、数字(Long)、布尔("true"),再统一用StringRedisTemplate操作 - 如果要用对象映射,必须显式配置
GenericJackson2JsonRedisSerializer,且类必须有无参构造、字段 public 或带 getter/setter
示例:安全的 Hash 写入
Map<String, String> fields = new HashMap<>();
fields.put("name", user.getName());
fields.put("age", String.valueOf(user.getAge()));
stringRedisTemplate.opsForHash().putAll("user:123", fields);Redis List 和 Java List 的边界在哪?
LPUSH/LRANGE 看起来像 Java 的 ArrayList,但千万别把它当内存 List 用——长度不受控、无索引优化、不支持随机更新。
立即学习“Java免费学习笔记(深入)”;
- Redis
List底层是双向链表,LINDEX时间复杂度是 O(N),查第 10000 个元素很慢;而 JavaArrayList.get(i)是 O(1) - 消息队列场景下,有人用
BLPOP+List<string></string>接收,却忘了BLPOP返回的是单个元素(String),不是List,强转必报ClassCastException - 分页需求(如评论列表)别用
LRANGE key 0 19加缓存,因为删除中间某条评论后,分页会错位;应改用Sorted Set配时间戳做范围查询
示例:正确消费 List
List<String> msg = stringRedisTemplate.opsForList().rightPop("queue:task", 1, TimeUnit.SECONDS);
// 注意:rightPop 返回的是 String,不是 List!上面这行是错的
// 正确写法是:
String item = stringRedisTemplate.opsForList().rightPop("queue:task"); // 返回单个 StringSet / Sorted Set 映射时最容易忽略的类型陷阱
SET 和 ZSET 在 Java 里常被映射成 Set<string></string> 或 Set<typedtuple>></typedtuple>,但实际使用中,类型擦除和序列化策略会让它们悄悄失效。
-
SetOperations.members("tag:123")返回的是Set<byte></byte>(如果用默认 Jdk 序列化),不是Set<string></string>,遍历时抛ClassCastException -
ZSetOperations.rangeWithScores()返回的Set<typedtuple>></typedtuple>,其中TypedTuple.getValue()是byte[],不是自动转好的String,需要手动 new String(...) - 如果用
ZSET做延时队列,score 用System.currentTimeMillis()没问题,但别用Instant.now().toEpochMilli()后再 toString() —— 多余转字符串会导致排序失效
示例:安全读取 ZSet
Set<ZSetOperations.TypedTuple<String>> tuples =
stringRedisTemplate.opsForZSet().rangeWithScores("delay:queue", 0, -1);
for (ZSetOperations.TypedTuple<String> t : tuples) {
String value = t.getValue(); // 这里 value 已是 String(用了 StringRedisTemplate)
double score = t.getScore();
}Redis 的五大数据类型和 Java 的映射,从来不是“类型对类型”的关系,而是“操作意图对操作能力”的匹配。真正卡住人的,往往不是不会写 hgetall,而是没想清楚:这个结构是否承担了它不该承担的职责——比如用 List 做分页,用 String 存对象,用 Set 做带序集合。










