第 2 章:Redis 数据结构
学习目标
- 掌握 5 种核心数据结构(STRING、HASH、LIST、SET、ZSET)
- 学会常用命令的 Java 写法
- 理解每种结构的适用场景
- 避开数据类型混用的坑
一、STRING(字符串)
最基础类型,value 可以是字符串、数字、二进制(图片,最大 512MB)。
bash
SET user:1001 "tom"
GET user:1001
INCR counter # 自增 1,返回新值
INCRBY counter 10 # 自增 10
DECR stock # 库存 -1
SETEX code 60 "1234" # 设置 60 秒过期
MSET k1 v1 k2 v2 # 批量设
MGET k1 k2 # 批量取Java 写法:
java
StringRedisTemplate redis = ...;
// 自增计数(文章阅读量)
Long views = redis.opsForValue().increment("article:1001:views");
// 批量
redis.opsForValue().multiSet(Map.of("k1", "v1", "k2", "v2"));
List<String> values = redis.opsForValue().multiGet(List.of("k1", "k2"));⚠️ 坑 1:
INCR操作的 key 必须能解析为整数,否则报ERR value is not an integer。存之前确认初始值是数字,或者用INCRBY0 先初始化。
二、HASH(哈希)
类似 Java 的 Map<String, String>,适合存对象。
bash
HSET user:1001 name "tom" age 20 city "beijing"
HGET user:1001 name # "tom"
HMGET user:1001 name age # 批量取
HGETALL user:1001 # 取所有字段
HINCRBY user:1001 age 1 # 年龄 +1
HDEL user:1001 city
HEXISTS user:1001 name # 1 / 0Java 写法:
java
HashOperations<String, String, String> hash = redis.opsForHash();
hash.putAll("user:1001", Map.of(
"name", "tom",
"age", "20",
"city", "beijing"
));
String name = hash.get("user:1001", "name");
Map<String, String> user = hash.entries("user:1001"); // 一次性取全部⚠️ 坑 2:Redis Hash 是扁平字段,不是嵌套结构。
user:1001:profile:name这种嵌套用JSON String更合适,Hash 适合字段经常单独读/写的场景。
三、LIST(列表)
双向链表,适合消息队列、最新列表。
bash
LPUSH news "msg1" # 左推
RPUSH news "msg2" # 右推
LRANGE news 0 -1 # 取所有
LPOP news # 左弹(获取并删除)
RPOP news # 右弹
LLEN news # 长度
BRPOP news 10 # 阻塞 10 秒右弹,没有元素就等Java 写法:
java
ListOperations<String, String> list = redis.opsForList();
list.leftPush("queue:order", "order-1001");
list.rightPush("queue:order", "order-1002");
String msg = list.leftPop("queue:order"); // 非阻塞
String msg2 = list.rightPop("queue:order", Duration.ofSeconds(10)); // 阻塞 10s⚠️ 坑 3:
LPOP在空列表返回nil,Java 里返回null—— 必须判空,否则NullPointerException。 ⚠️ 坑 4:Redis List 做消息队列没有 ACK,消费者崩溃消息会丢。生产环境用 Stream 或专业 MQ。
四、SET(集合)
无序去重集合,适合标签、共同好友。
bash
SADD tags:1001 "java" "redis" "mysql"
SMEMBERS tags:1001 # 所有成员
SISMEMBER tags:1001 "java" # 1 / 0
SCARD tags:1001 # 成员数
SINTER tags:1001 tags:1002 # 交集(共同标签)
SUNION tags:1001 tags:1002 # 并集
SDIFF tags:1001 tags:1002 # 差集(1001 独有的)Java 写法:
java
SetOperations<String, String> set = redis.opsForSet();
set.add("tags:1001", "java", "redis");
Boolean isMember = set.isMember("tags:1001", "java");
Set<String> inter = set.intersect("tags:1001", "tags:1002");五、ZSET(有序集合)
成员 + 分数(score),按分数排序,适合排行榜。
bash
ZADD rank:2024 95 "tom" 88 "jerry" 76 "alice"
ZREVRANGE rank:2024 0 9 WITHSCORES # 前 10 名(分数从高到低)
ZRANGE rank:2024 0 -1 # 全部(分数从低到高)
ZSCORE rank:2024 "tom" # 95
ZINCRBY rank:2024 5 "alice" # 加 5 分
ZRANK rank:2024 "tom" # 排名(从 0 开始)Java 写法:
java
ZSetOperations<String, String> zset = redis.opsForZSet();
// 加分(热门文章)
zset.incrementScore("article:hot", 1, "1001");
// 取前 10
Set<ZSetOperations.TypedTuple<String>> top = zset.reverseRangeWithScores("article:hot", 0, 9);
top.forEach(t -> System.out.println(t.getValue() + ":" + t.getScore()));⚠️ 坑 5:ZSet 的 score 是 double,精度有限 —— 不要存金额(用整数分),存"阅读量、点赞数"这种大致排序没问题。
六、数据结构速查表
| 类型 | 命令前缀 | 典型场景 |
|---|---|---|
| STRING | SET/GET/INCR | 缓存、计数器、分布式 ID |
| HASH | HSET/HGET/HGETALL | 对象、用户资料 |
| LIST | LPUSH/RPOP | 简单队列、最新列表 |
| SET | SADD/SINTER | 标签、共同好友 |
| ZSET | ZADD/ZRANGE | 排行榜、加权队列 |
七、本章小结
| 要点 | 关键 |
|---|---|
| STRING | 自增/批量,适合计数和缓存 |
| HASH | 字段独立读写,比 JSON 灵活 |
| LIST | 简单队列,无 ACK 不适合生产 |
| SET | 去重 + 集合运算 |
| ZSET | 排行榜,score 不要存金额 |
动手练习
- 文章阅读量计数器:
INCR article:{id}:views,Java 实现 - 商品标签系统:用 Set 存,实现"两个商品的共同标签"
- 热门商品榜:ZSet 存商品 ID + 销量分,取 Top 10
下一章:第 3 章:Redis 持久化 →