WD
Classnote Docs课程课件
19

Redis 教程

学习目标:

  • 理解 Redis 的定位、特点与典型应用场景
  • 掌握 String、List、Set、Hash、Sorted Set 等常用数据结构
  • 熟悉持久化、内存淘汰策略与常见缓存问题
  • 能在 Java / Spring Boot 中完成 Redis 基础整合与使用
  • 能用 Redis 解决缓存、计数器、排行榜等常见问题

本章重点:

  • Redis 与关系型数据库的差异
  • 五大核心数据结构与适用场景
  • RDB / AOF 持久化与内存淘汰策略
  • 缓存穿透、击穿、雪崩三大问题的处理思路
01 / Section

1. 引言

1.1 Web发展历程

迄今为止,互联网的发展已经经历了两个阶段:Web 1.0 和 Web 2.0。

阶段 特点 代表
Web 1.0 静态网站,单方面信息传递,无互动 搜狐、新浪、网易
Web 2.0 内容互动,用户成为内容提供方 微博、B站、抖音

1.2 传统关系型数据库的挑战

进入 Web 2.0 时代后,数据爆炸式增长,传统关系型数据库面临以下挑战:

High Performance - 高并发写需求

Web 2.0 网站需要根据用户个性化信息实时生成动态页面,数据库并发负载极高(每秒上万次读写请求)。关系型数据库难以承受如此高的硬盘 IO 压力。

Huge Storage - 海量数据存储

大型 SNS 网站每天产生海量用户动态。以 FriendFeed 为例,一个月就产生 2.5 亿条动态。在单表 2.5 亿条记录中进行 SQL 查询,效率极其低下。

1.3 NoSQL的诞生

Web 2.0 应用中,关系型数据库的许多特性反而成为负担:

  • 很多实时系统不要求严格的事务
  • 对读写一致性要求较低
  • 避免多表关联查询,多为单表主键查询

因此,NoSQL 数据库应运而生。

Redis 是典型的 NoSQL 数据库,与传统关系型数据库相比:

  • 纯内存存储
  • Key-Value 键值对结构
  • 非结构化数据
02 / Section

2. Redis简介

2.1 什么是Redis

Redis(Remote Dictionary Server)是一个使用 C 语言编写的、开源的、支持网络的、基于内存的、可持久化的 Key-Value 非关系型数据库。

2.2 Redis核心特点

特性 说明
C语言编写 性能极高,单机可达 10万+ QPS
开源免费 GitHub: https://github.com/redis
基于内存 数据存储在内存中,读写速度极快
可持久化 支持 RDB 和 AOF 两种持久化方式
Key-Value 以键值对方式存储数据
支持网络 客户端通过网络连接服务端
多语言API 支持 Java、Python、Go、Node.js 等

2.3 参考资源

  • 中文官网: https://www.redis.net.cn/
  • 命令参考: http://doc.redisfans.com/
  • 官方文档: https://redis.io/documentation
03 / Section

3. 安装与启动

⚠️ 注意: Redis 官方不支持 Windows 操作系统,但 Windows 团队提供了适配版本。

3.1 Windows安装

下载

从 GitHub 下载 Windows 适配版本:

  • 地址: https://github.com/microsoftarchive/redis/releases
Redis Windows 版本下载示意
Redis Windows 版本下载示意

解压

解压后目录结构:

text
redis/
├── redis-server.exe    # 服务端程序
├── redis-cli.exe       # 客户端程序
├── redis.windows.conf  # 配置文件
└── ...
Redis 解压目录示意
Redis 解压目录示意

启动服务端

bash
# 方式1: 使用默认配置
redis-server.exe

# 方式2: 指定配置文件(推荐)
redis-server.exe redis.windows.conf

启动成功后会看到如下输出:

text
[*****] # Server initialized
[*****] * Ready to accept connections
Redis 服务端启动示意
Redis 服务端启动示意

启动客户端

bash
# 连接本地默认端口
redis-cli.exe

# 连接指定主机和端口
redis-cli.exe -h localhost -p 6379

# 连接带密码的Redis
redis-cli.exe -h localhost -p 6379 -a password

3.2 Docker安装

如果本机已经安装了 Docker,那么使用容器启动 Redis 会更快捷

bash
# 拉取官方镜像
docker pull redis:7.2-alpine

# 启动 Redis 容器
docker run -d \
  --name redis-demo \
  -p 6379:6379 \
  -v /docker/redis-data:/data \
  redis:7.2-alpine \
  redis-server --appendonly yes

# 查看容器状态
docker ps

# 进入 Redis 命令行
docker exec -it redis-demo redis-cli

# 测试连接
PING

常用管理命令:

bash
# 停止容器
docker stop redis-demo

# 启动已存在的容器
docker start redis-demo

# 删除容器(删除前先停止)
docker rm -f redis-demo

3.3 图形化客户端

除了命令行客户端 redis-cli,我们也可以使用图形化工具来连接 Redis,查看 key、修改数据和观察数据结构。

一个常见选择是 Another Redis Desktop Manager

在学习阶段,图形化客户端比较适合做这些事:

  • 直观看 Redis 中有哪些 key
  • 查看 String、Hash、List、Set、Sorted Set 的实际存储效果
  • 辅助验证命令执行结果
Another Redis Desktop Manager 连接示意
Another Redis Desktop Manager 连接示意
Another Redis Desktop Manager 数据浏览示意
Another Redis Desktop Manager 数据浏览示意

💡 建议:

  • 初学命令时优先使用 redis-cli
  • 观察数据结构和排查数据时,再配合图形化客户端一起使用
  • 图形化工具方便,但不要因此忽略 Redis 命令本身
04 / Section

4. 核心配置

4.1 常规配置

编辑 redis.confredis.windows.conf

ini
# 是否以守护进程方式运行(Linux)
daemonize no

# 客户端超时时间(0表示不超时)
timeout 0

# 端口号(默认6379)
port 6379

# 绑定地址
bind 127.0.0.1
# bind 0.0.0.0  # 允许所有IP连接(生产环境慎用)

# 日志级别: debug | verbose | notice | warning
loglevel notice

# 数据库数量(默认16个)
databases 16

# 设置密码
requirepass your_password

4.2 持久化配置

RDB(Redis Database)

RDB 通过内存快照的方式持久化数据,是 Redis 默认的持久化策略。

配置示例:

ini
# 快照保存目录
dir /var/lib/redis

# 快照文件名
dbfilename dump.rdb

# 触发策略: save <秒> <变化次数>
save 900 1      # 900秒内至少1次修改
save 300 10     # 300秒内至少10次修改
save 60 10000   # 60秒内至少10000次修改
RDB 配置示意
RDB 配置示意

RDB特点:

  • ✅ 保存速度快,文件体积小
  • ✅ 恢复速度快
  • ❌ 可能丢失最后一次快照后的数据

💡 可以这样理解 RDB:

  • RDB 像是给某一个时刻的内存状态拍了一张完整快照
  • 每次保存的是当时的全量数据,不是增量日志
  • 因为保存的是快照,所以备份和恢复都比较快
  • 但如果 Redis 在下一次快照前宕机,就可能丢失这段时间的新数据

AOF(Append Only File)

AOF 通过追加写入命令的方式持久化数据。

配置示例:

ini
# 开启AOF
appendonly yes

# AOF文件路径(与RDB共用dir配置)
dir /var/lib/redis

# AOF文件名
appendfilename "appendonly.aof"

# 同步策略
# appendfsync always    # 每条命令都同步(最安全,最慢)
appendfsync everysec    # 每秒同步(推荐)
# appendfsync no        # 由操作系统决定

AOF特点:

  • ✅ 数据安全性高(可做到不丢数据)
  • ❌ 文件体积大
  • ❌ 恢复速度慢(需重放命令)

💡 可以这样理解 AOF:

  • AOF 不保存某一刻的完整状态,而是把写命令持续追加到日志文件里
  • 恢复数据时,本质上就是把这些写命令重新执行一遍
  • 因为记录得更细,所以数据安全性通常比 RDB 更高
  • 但文件会更大,恢复也会更慢一些

💡 最佳实践:

  • 生产环境建议同时开启 RDB 和 AOF
  • 两者同时开启时,Redis 恢复数据通常优先使用 AOF
  • 课堂理解上可以记成一句话:RDB 更像快照,AOF 更像操作日志
05 / Section

5. 数据结构

在正式学习 Redis 数据结构之前,先记住一个最核心的认识:

Redis 存储的本质是键值对,也就是 key -> value

这里说的“数据结构”,多数时候主要指的是 value 的结构,比如:

  • value 是一个普通字符串,对应 String
  • value 是一个列表,对应 List
  • value 是一个集合,对应 Set
  • value 是一个键值对集合,对应 Hash
  • value 是一个带分数的有序集合,对应 Sorted Set

key 本身通常只是一个字符串,但在实际开发中,我们会把它设计得更有层次,常见写法是用冒号 : 分隔多级含义。

例如:

text
user:1001:name
user:1001:cart
order:20240501:10001
article:1001:view

这种命名方式的好处是:

  • 语义清晰,看到 key 基本就知道它表示什么数据
  • 方便按前缀查询,例如 KEYS user:*
  • 便于团队统一命名规范,减少后期维护成本

5.1 通用命令

bash
# 切换数据库(0-15,默认0)
SELECT index

# 密码认证
AUTH password

# 查找key(支持通配符: * ? [])
KEYS pattern
KEYS *          # 查看所有key
KEYS user:*     # 查看前缀为user:的key

# 清空所有数据库(危险操作!)
FLUSHALL

# 清空当前数据库
FLUSHDB

# 删除key
DEL key

# 查看key类型
TYPE key

# 设置key过期时间(秒)
EXPIRE key seconds

# 查看key剩余生存时间
TTL key

5.2 String(字符串)

最基础的数据类型,可存储字符串、数字。

String 数据结构示意
String 数据结构示意

基本命令

bash
# 设置key-value(key存在则覆盖)
SET key value

# 获取value
GET key

# 批量设置
MSET key1 value1 key2 value2 ...

# 批量获取
MGET key1 key2 ...

# 数值+1
INCR key

# 数值+指定步长
INCRBY key increment

# 数值-1
DECR key

# 数值-指定步长
DECRBY key decrement

# 设置key并指定过期时间(秒)
SETEX key seconds value

# 仅当key不存在时才设置
SETNX key value

应用场景

场景 实现方式 指令说明
缓存 SET user:1001 "{...}" + EXPIRE 先写入缓存数据,再设置过期时间
计数器 INCR page:view:homepage 对访问次数做原子自增
限流 INCR user:1001:api:count + EXPIRE 60 在固定时间窗口内统计请求次数

5.3 List(列表)

双向链表实现,支持两端插入/弹出。

List 数据结构示意
List 数据结构示意

基本命令

bash
# 从左侧插入
LPUSH key value1 value2 ...

# 从左侧弹出
LPOP key

# 从右侧插入
RPUSH key value1 value2 ...

# 从右侧弹出
RPOP key

# 获取列表长度
LLEN key

# 获取指定范围元素(0表示第一个,-1表示最后一个)
LRANGE key start stop

# 获取指定索引元素
LINDEX key index

# 在指定元素前/后插入
LINSERT key BEFORE|AFTER pivot value

# 修改指定索引元素
LSET key index value

# 删除指定元素(count>0从头删,count<0从尾删,count=0全删)
LREM key count value

应用场景

场景 实现方式 指令说明
消息队列 LPUSH + BRPOP(阻塞弹出) 一端写入消息,另一端阻塞等待并消费消息
最新动态 LPUSH timeline:user:1001 "..." + LTRIM 0 99 头部插入最新内容,并只保留最近100条
LPUSH + LPOP 同一端进出,后进先出
队列 LPUSH + RPOP 一端进入另一端取出,先进先出

5.4 Set(集合)

无序、不重复的字符串集合。

Set 数据结构示意
Set 数据结构示意

基本命令

bash
# 添加成员
SADD key member1 member2 ...

# 获取成员数量
SCARD key

# 获取所有成员
SMEMBERS key

# 判断成员是否存在
SISMEMBER key member

# 随机弹出指定数量成员
SPOP key [count]

# 随机获取指定数量成员(不删除)
SRANDMEMBER key [count]

# 求交集
SINTER key1 key2 ...

# 求交集并保存
SINTERSTORE destination key1 key2 ...

# 求并集
SUNION key1 key2 ...

# 求并集并保存
SUNIONSTORE destination key1 key2 ...

# 求差集
SDIFF key1 key2 ...

# 求差集并保存
SDIFFSTORE destination key1 key2 ...

# 移动成员到另一个集合
SMOVE source destination member

# 删除成员
SREM key member1 member2 ...

应用场景

场景 实现方式 指令说明
共同好友 SINTER user:1001:friends user:1002:friends 求两个好友集合的交集
好友推荐 SDIFF user:1001:friends user:1002:friends 求差集,找出只在一方集合中的成员
标签系统 SADD article:1001:tags "java" "redis" 给文章绑定多个不重复标签
抽奖 SRANDMEMBER prize:pool 3 随机取出若干奖池成员
去重 SADD ip:2024-01-01 "192.168.1.1" 利用集合成员唯一性做去重

5.5 Hash(哈希)

键值对集合,适合存储对象。

Hash 数据结构示意
Hash 数据结构示意

基本命令

bash
# 设置字段
HSET key field value

# 获取字段值
HGET key field

# 批量设置
HMSET key field1 value1 field2 value2 ...

# 批量获取
HMGET key field1 field2 ...

# 判断字段是否存在
HEXISTS key field

# 获取所有字段和值
HGETALL key

# 获取所有字段
HKEYS key

# 获取所有值
HVALS key

# 获取字段数量
HLEN key

# 字段值增加指定数值
HINCRBY key field increment

# 仅当字段不存在时才设置
HSETNX key field value

# 删除字段
HDEL key field1 field2 ...

应用场景

场景 实现方式 指令说明
用户信息 HSET user:1001 name "张三" age 25 把同一个对象的多个字段存进一个 hash
购物车 HSET cart:user:1001 product:10001 2 用 field 表示商品,用 value 表示数量
配置信息 HSET config:app version "1.0.0" env "prod" 将一组相关配置集中存储在同一个 hash 中

5.6 Sorted Set(有序集合)

每个成员关联一个分数,按分数排序。

Sorted Set 数据结构示意
Sorted Set 数据结构示意

基本命令

bash
# 添加成员(分数在前)
ZADD key score1 member1 score2 member2 ...

# 获取成员数量
ZCARD key

# 获取指定分数区间成员数量
ZCOUNT key min max

# 获取成员分数
ZSCORE key member

# 增加成员分数
ZINCRBY key increment member

# 按排名升序获取(0表示第一名)
ZRANGE key start stop [WITHSCORES]

# 按排名降序获取
ZREVRANGE key start stop [WITHSCORES]

# 按分数升序获取
ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]

# 按分数降序获取
ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count]

# 获取成员升序排名
ZRANK key member

# 获取成员降序排名
ZREVRANK key member

# 删除成员
ZREM key member1 member2 ...

# 删除指定排名区间成员
ZREMRANGEBYRANK key start stop

# 删除指定分数区间成员
ZREMRANGEBYSCORE key min max

应用场景

场景 实现方式 指令说明
排行榜 ZADD leaderboard 100 "player1" 用分数表示排名依据,成员自动按分数排序
延时队列 ZADD delay:queue timestamp "task" 用时间戳做 score,到时间后再取出任务
范围查询 ZRANGEBYSCORE articles 1700000000 1700086400 按 score 区间筛选成员
热度排序 ZINCRBY hot:articles 1 "article:1001" 每次访问就给文章热度加分,再按分数排序
06 / Section

6. 内存淘汰策略

当内存达到上限时,Redis 会触发淘汰策略删除旧数据。

6.1 淘汰策略分类

volatile 系列(针对设置了过期时间的key)

策略 说明
volatile-lru 淘汰最近最少使用的key
volatile-lfu 淘汰一段时间内使用频率最低的key
volatile-random 随机淘汰
volatile-ttl 淘汰即将过期的key

allkeys 系列(针对所有key)

策略 说明
allkeys-lru 淘汰最近最少使用的key
allkeys-lfu 淘汰一段时间内使用频率最低的key
allkeys-random 随机淘汰

其他

策略 说明
noeviction 不淘汰,新写入直接报错(默认)

6.2 配置方法

ini
# 配置最大内存
maxmemory 256mb

# 配置淘汰策略
maxmemory-policy allkeys-lru

6.3 策略选择建议

场景 推荐策略
缓存场景 allkeys-lruallkeys-lfu
需要保留热数据 allkeys-lfu
时效性数据 volatile-ttl
不允许丢数据 noeviction

6.4 适合存储在Redis中的数据特征

  • ✅ 访问频率高(QPS大)
  • ✅ 对丢失不敏感
  • ✅ 数据量相对较小
  • ✅ 需要快速读写
07 / Section

7. Java客户端

Redis 和 MySQL 一样,都是典型的 C/S 架构应用。

除了 redis-cli 这样的命令行客户端之外,也有图形化客户端和 Java 客户端。

在 Java 开发里,最常见的客户端选择有:

  • Jedis:贴近 Redis 原生命令,课堂中以了解为主
  • Lettuce:Spring Boot 默认集成较多,支持异步和响应式

7.1 客户端对比

客户端 特点 推荐场景
Jedis 简单直接,API与Redis命令对应 了解原生命令映射

7.2 Jedis使用

这一部分以了解为主,重点是知道 Jedis 和 Redis 原生命令的对应关系,不作为本课的主要实战客户端。

Maven依赖(推荐4.x版本)

xml
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>4.4.3</version>
</dependency>

基础使用

java
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

public class JedisDemo {
    
    // 单连接(不推荐生产使用)
    public void basicUsage() {
        Jedis jedis = new Jedis("localhost", 6379);
        jedis.auth("password");  // 如果有密码
        
        jedis.set("key", "value");
        String value = jedis.get("key");
        System.out.println(value);
        
        jedis.close();
    }
    
    // 连接池(推荐)
    public void poolUsage() {
        JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxTotal(100);      // 最大连接数
        config.setMaxIdle(50);        // 最大空闲连接
        config.setMinIdle(10);        // 最小空闲连接
        
        try (JedisPool pool = new JedisPool(config, "localhost", 6379, 3000, "password");
             Jedis jedis = pool.getResource()) {
            
            jedis.set("pool:key", "value");
            String value = jedis.get("pool:key");
            System.out.println(value);
        }
    }
}

保存对象时的常见做法

Jedis 最适合直接操作字符串、数字和 Redis 原生命令。

如果业务里要保存一个 Java 对象,常见做法通常不是把对象直接丢进去,而是先转成 JSON 字符串再存入 Redis。

java
import com.fasterxml.jackson.databind.ObjectMapper;
import redis.clients.jedis.Jedis;

public class JedisJsonDemo {

    private static final ObjectMapper MAPPER = new ObjectMapper();

    public void saveUser() throws Exception {
        try (Jedis jedis = new Jedis("localhost", 6379)) {
            jedis.auth("password");

            User user = new User(1L, "zhangsan", "zhangsan@example.com");
            String json = MAPPER.writeValueAsString(user);

            jedis.set("user:1", json);
        }
    }

    public User getUser() throws Exception {
        try (Jedis jedis = new Jedis("localhost", 6379)) {
            jedis.auth("password");

            String json = jedis.get("user:1");
            return json == null ? null : MAPPER.readValue(json, User.class);
        }
    }
}

这个例子想说明的重点不是 ObjectMapper 本身,而是两件事:

  • Jedis 的方法名通常和 Redis 命令一一对应,学习成本低
  • 当你要存复杂对象时,对象 -> JSON -> Redis 是非常常见的一条路线

操作各数据类型

java
public void dataTypeOperations(Jedis jedis) {
    // String
    jedis.set("user:1:name", "张三");
    jedis.expire("user:1:name", 3600);
    
    // Hash
    jedis.hset("user:1", "name", "张三");
    jedis.hset("user:1", "age", "25");
    Map<String, String> user = jedis.hgetAll("user:1");
    
    // List
    jedis.lpush("queue:tasks", "task1", "task2");
    String task = jedis.rpop("queue:tasks");
    
    // Set
    jedis.sadd("tags:article:1", "java", "redis");
    Set<String> tags = jedis.smembers("tags:article:1");
    
    // Sorted Set
    jedis.zadd("rank:score", 100, "player1");
    jedis.zadd("rank:score", 95, "player2");
    Set<Tuple> topPlayers = jedis.zrevrangeWithScores("rank:score", 0, 9);
}
08 / Section

8. Spring Boot整合Redis

8.1 添加依赖

xml
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

<!-- 连接池 -->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>

8.2 配置文件

说明:以下示例使用 Spring Boot 3.x 的配置前缀 spring.data.redis.*

yaml
spring:
  data:
    redis:
      host: localhost
      port: 6379
      password:
      database: 0
      timeout: 3000ms
      lettuce:
        pool:
          max-active: 8      # 最大连接数
          max-idle: 8        # 最大空闲连接
          min-idle: 0        # 最小空闲连接
          max-wait: 1000ms   # 最大等待时间

8.3 使用StringRedisTemplate

这一部分是 Spring Boot 整合 Redis 的主线内容,重点掌握日常字符串、哈希、列表、集合和有序集合操作。

java
@Service
public class RedisService {
    
    @Autowired
    private StringRedisTemplate redisTemplate;
    
    // String操作
    public void stringOps() {
        redisTemplate.opsForValue().set("key", "value");
        redisTemplate.opsForValue().set("key", "value", 30, TimeUnit.SECONDS);
        String value = redisTemplate.opsForValue().get("key");
    }
    
    // Hash操作
    public void hashOps() {
        redisTemplate.opsForHash().put("user:profile:1", "name", "张三");
        redisTemplate.opsForHash().put("user:profile:1", "age", "25");
        Map<Object, Object> entries = redisTemplate.opsForHash().entries("user:profile:1");
    }
    
    // List操作
    public void listOps() {
        redisTemplate.opsForList().leftPush("queue", "task1");
        String task = redisTemplate.opsForList().rightPop("queue");
    }
    
    // Set操作
    public void setOps() {
        redisTemplate.opsForSet().add("tags", "java", "redis");
        Set<String> tags = redisTemplate.opsForSet().members("tags");
    }
    
    // Sorted Set操作
    public void zsetOps() {
        redisTemplate.opsForZSet().add("rank", "player1", 100);
        Set<ZSetOperations.TypedTuple<String>> top = 
            redisTemplate.opsForZSet().reverseRangeWithScores("rank", 0, 9);
    }
}

8.4 使用RedisTemplate(对象序列化)

💡 课堂定位:这一部分作为补充了解,重点知道当 value 不再是简单字符串时,通常需要考虑序列化方式。

java
@Configuration
public class RedisConfig {
    
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        
        // 使用JSON序列化,避免 Jackson 版本差异带来的编译问题
        StringRedisSerializer stringSerializer = new StringRedisSerializer();
        GenericJackson2JsonRedisSerializer jsonSerializer = new GenericJackson2JsonRedisSerializer();
        
        // 设置key和value的序列化方式
        template.setKeySerializer(stringSerializer);
        template.setValueSerializer(jsonSerializer);
        template.setHashKeySerializer(stringSerializer);
        template.setHashValueSerializer(jsonSerializer);
        
        template.afterPropertiesSet();
        return template;
    }
}

使用示例

配置好 RedisTemplate<String, Object> 之后,就可以直接把普通 Java 对象作为 value 存入 Redis。

java
public class User {
    private Integer id;
    private String name;
    private Integer age;

    public User() {
    }

    public User(Integer id, String name, Integer age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}
java
@Service
public class UserRedisService {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    public void saveUser() {
        User user = new User(1, "张三", 25);
        redisTemplate.opsForValue().set("user:object:1", user);
        redisTemplate.expire("user:object:1", 30, TimeUnit.MINUTES);
    }

    public User getUser() {
        Object obj = redisTemplate.opsForValue().get("user:object:1");
        return obj == null ? null : (User) obj;
    }
}
java
@RestController
@RequestMapping("/redis/user")
public class UserRedisController {

    @Autowired
    private UserRedisService userRedisService;

    @GetMapping("/save")
    public String saveUser() {
        userRedisService.saveUser();
        return "保存成功";
    }

    @GetMapping
    public User getUser() {
        return userRedisService.getUser();
    }
}

说明

  • 写入时,User 对象会先被序列化成 JSON 再存入 Redis
  • 读取时,RedisTemplate 会按配置的序列化器把 JSON 反序列化回 Java 对象
  • 同一个 key 只能对应一种数据类型,例如 user:profile:1 用作 Hash 时,就不要再拿 user:profile:1 去存普通字符串或对象,否则会报 WRONGTYPE Operation against a key holding the wrong kind of value
  • 如果项目里只是保存简单字符串,优先使用 StringRedisTemplate;只有在需要直接存对象时,再考虑 RedisTemplate<String, Object>
09 / Section

9. 附录:数据结构应用场景速查表

数据结构 主要应用场景 关键优势
String 缓存、计数器 原子操作、可设置过期
Hash 对象存储、购物车 节省内存、字段独立更新
List 消息队列、时间线 双向操作、阻塞读取
Set 去重、社交关系 天然去重、集合运算
Sorted Set 排行榜、范围查询 自动排序、范围查询