Skip to content
第 2 章 ⏱ 13 分钟阅读

第 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。存之前确认初始值是数字,或者用 INCRBY 0 先初始化。

二、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 / 0

Java 写法:

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,精度有限 —— 不要存金额(用整数分),存"阅读量、点赞数"这种大致排序没问题。

六、数据结构速查表 ​

类型命令前缀典型场景
STRINGSET/GET/INCR缓存、计数器、分布式 ID
HASHHSET/HGET/HGETALL对象、用户资料
LISTLPUSH/RPOP简单队列、最新列表
SETSADD/SINTER标签、共同好友
ZSETZADD/ZRANGE排行榜、加权队列

七、本章小结 ​

要点关键
STRING自增/批量,适合计数和缓存
HASH字段独立读写,比 JSON 灵活
LIST简单队列,无 ACK 不适合生产
SET去重 + 集合运算
ZSET排行榜,score 不要存金额

动手练习 ​

  1. 文章阅读量计数器:INCR article:{id}:views,Java 实现
  2. 商品标签系统:用 Set 存,实现"两个商品的共同标签"
  3. 热门商品榜:ZSet 存商品 ID + 销量分,取 Top 10

下一章:第 3 章:Redis 持久化 →

本站基于 VitePress 构建 · 由 StackHub 团队维护