结合苍穹外卖对redis进行总结和教学

Redis 学习笔记

hi,随着ai的迅速发展,我认为学习一项技术不能花太长时间,所以我结合最简单的苍穹外卖来对redis进行一个系统性的讲述,让你两天就能够快速的入门

一、Redis 在苍穹外卖中的作用

1. 存储店铺营业状态

SHOP_STATUS 这个 key 存储店铺是"营业中"还是"打烊中",admin 端设置,user 端查询,双方读写同一个 key。

对应文件:

  • admin/ShopController.java --- 设置 + 查询
  • user/ShopController.java --- 只查询
java 复制代码
// 设置状态
redisTemplate.opsForValue().set(KEY, status);

// 查询状态
Integer status = (Integer) redisTemplate.opsForValue().get(KEY);

2. 菜品列表缓存(核心用途)

用户按分类查菜品时,先查 Redis 缓存,有就直接返回,没有再查数据库并写入缓存。

对应文件: user/DishController.java

java 复制代码
String key = "dish_" + categoryId;

// 先查缓存
List<DishVO> list = (List<DishVO>) redisTemplate.opsForValue().get(key);
if (list != null && list.size() > 0) {
    return Result.success(list);  // 缓存命中,直接返回
}

// 缓存未命中,查数据库
list = dishService.listWithFlavor(dish);
redisTemplate.opsForValue().set(key, list);  // 写入缓存
return Result.success(list);

缓存清理admin/DishController.java):

后台增删改菜品时,调用 cleanCache() 把相关缓存全部清掉,保证数据一致性。

java 复制代码
private void cleanCache(String pattern) {
    Set keys = redisTemplate.keys(pattern);
    redisTemplate.delete(keys);
}

// 新增菜品 → 清理该分类缓存
cleanCache("dish_" + categoryId);

// 删除/修改菜品 → 清理所有菜品缓存
cleanCache("dish_*");

二、什么时候使用 Redis

核心原则:读多写少、数据变化不频繁 的场景。

适用场景

场景 原因 本项目例子
缓存热点数据 减少数据库压力,加快响应 菜品列表、分类列表、店铺状态
存共享状态 多个接口/服务都要读写同一份数据 店铺营业状态(admin 写,user 读)
验证码/临时数据 需要自动过期 短信验证码(set(key, code, 5, MINUTES)
计数/限流 需要原子操作 接口访问频率控制

不适用的场景

  • 数据量大且查询条件复杂 → 用数据库
  • 数据实时性要求极高、不能有缓存延迟 → 直接查库
  • 写入频繁、读取很少 → 缓存收益不大

判断口诀

问自己三个问题:

  1. 这个数据被频繁读取吗? → 是 → 考虑缓存
  2. 数据变化慢吗? → 是 → 适合缓存
  3. 允许短暂的不一致吗? → 是 → 可以用 Redis

三个都满足,就放心用 Redis。


三、RedisTemplate 常用 API

java 复制代码
@Autowired
private RedisTemplate redisTemplate;

// String 类型(项目里用得最多)
redisTemplate.opsForValue().set(key, value);                          // 写入
redisTemplate.opsForValue().get(key);                                 // 读取
redisTemplate.opsForValue().set(key, value, 30, TimeUnit.MINUTES);   // 带过期时间

// 删除
redisTemplate.delete(key);         // 删除单个
redisTemplate.delete(keys);        // 批量删除(传入 Set)

// 按模式查找 key
redisTemplate.keys("dish_*");      // 返回所有匹配的 key 集合

// 判断 key 是否存在
redisTemplate.hasKey(key);

四、项目 Redis 配置

application-dev.yml:

yaml 复制代码
redis:
  host: localhost
  port: 6379
  password: 123456
  database: 10   # 使用第 10 号库

RedisConfiguration.java 中设置了 key 序列化方式:

java 复制代码
redisTemplate.setKeySerializer(new StringRedisSerializer());

把 key 的序列化方式从默认的 JDK 序列化改成字符串序列化,Redis 里看到的 key 是可读的 dish_1,而不是乱码字节。


五、Day 1 上午:认识 Redis + String 类型

1. Redis 是什么

Redis 是一个内存数据库,数据存在内存里,读写速度极快。

它存储数据的方式是 键值对(Key-Value),就像一本字典:

vbnet 复制代码
key → value
"username" → "zhangsan"
"age" → 25

打个比方:

  • MySQL = 图书馆找一本书(要去书架上翻,慢)
  • Redis = 你桌上的便签纸(低头就看到,快)

2. Redis vs MySQL

MySQL Redis
存储位置 硬盘 内存
速度 毫秒级 微秒级
数据结构 表/行/列 键值对
持久化 天然持久 需要配置

3. 为什么需要 Redis

假设一个接口每秒被调用 1000 次,每次都查 MySQL,数据库压力很大。

但这个数据其实很少变化(比如菜品分类),那查一次 MySQL,把结果抄一份到 Redis,下次直接从 Redis 读,数据库就轻松了。

这就是缓存,也是项目里 Redis 最核心的用途。

4. String 类型基本操作

String 是 Redis 最基础、用得最多的数据类型,项目里的菜品缓存和店铺状态都是用它存的。

核心命令:

命令 作用 场景
SET key value 写入 通用
GET key 读取 通用
DEL key 删除 通用
SETEX key 秒 value 写入并设过期时间 验证码、临时缓存
TTL key 查剩余过期时间 检查缓存状态
INCR key 数字+1 计数器
DECR key 数字-1 计数器

基本读写:

vbnet 复制代码
SET name zhangsan    → OK        (存入成功)
GET name             → zhangsan  (读取到值)
DEL username         → 1         (删除了1个key)
GET username         → (空)       (已删除)

带过期时间(验证码场景):

yaml 复制代码
SETEX phone_code 10 8888   → OK     (写入,10秒后自动消失)
GET phone_code             → 8888   (10秒内可以读到)
TTL phone_code             → 10     (还剩10秒存活)

TTL 的含义:正数 = 还剩多少秒,-1 = 永不过期,-2 = 已经不存在了。

计数器:

sql 复制代码
SET count 0     → 初始值为 0
INCR count      → 1   (自动+1)
INCR count      → 2
INCR count      → 3
GET count       → 3

INCR 是原子操作,多个人同时操作也不会算错。

5. 对应到项目的代码

java 复制代码
// 存店铺状态 --- 就是 SET
redisTemplate.opsForValue().set("SHOP_STATUS", 1);

// 读店铺状态 --- 就是 GET
Integer status = (Integer) redisTemplate.opsForValue().get("SHOP_STATUS");

// 存菜品缓存 --- 就是 SET
redisTemplate.opsForValue().set("dish_" + categoryId, list);

// 读菜品缓存 --- 就是 GET
List<DishVO> list = (List<DishVO>) redisTemplate.opsForValue().get("dish_" + categoryId);

// 清缓存 --- 就是 DEL
redisTemplate.delete(keys);

opsForValue() 就是操作 String 类型的意思。

6. 上午小结

  1. Redis 是内存里的字典,key→value,读写极快
  2. String 是最基础的类型,SET 写、GET 读、DEL 删
  3. SETEX 可以加过期时间,到时间自动消失,适合验证码和缓存

六、Day 1 下午:List 列表类型 + Hash 哈希类型

1. List 列表类型

List 是一个有序的队列,你可以从左边或右边往里塞东西,也可以从两边往外拿。

想象一根吸管,从左边塞珠子进去,也可以从右边塞,拿的时候也从两头拿。

核心命令:

命令 作用
LPUSH key value 从左边插入
RPUSH key value 从右边插入
LPOP key 从左边弹出
RPOP key 从右边弹出
LRANGE key start stop 查看范围(0 -1 表示全部)
LLEN key 获取列表长度

演示:

css 复制代码
LPUSH mylist a   → 从左边插入 a      → 列表:[a]
LPUSH mylist b   → 从左边插入 b      → 列表:[b, a]
RPUSH mylist c   → 从右边插入 c      → 列表:[b, a, c]
LRANGE 0 -1      → 查看全部           → b  a  c

LPOP mylist      → 从左边拿走 b      → 列表:[a, c]
RPOP mylist      → 从右边拿走 c      → 列表:[a]
LLEN mylist      → 长度是 1

使用场景: 消息队列、最近浏览记录、排队系统


2. Hash 哈希类型

Hash 就像一个对象,一个 key 里面可以存多个字段。

对比 String:

  • String 是一张便签只写一个值
  • Hash 是一张表格可以填多个格子
yaml 复制代码
String:name → zhangsan        (一张便签只存一个信息)

Hash:  user:1 → {
            name: zhangsan,    (一个对象存多个字段)
            age: 20,
            city: beijing
        }

核心命令:

命令 作用
HSET key field value 设置字段
HGET key field 获取单个字段
HGETALL key 获取所有字段和值
HDEL key field 删除字段
HKEYS key 获取所有字段名
HVALS key 获取所有值

演示:

sql 复制代码
HSET user:1 name zhangsan   → 存入姓名,成功
HSET user:1 age 20          → 存入年龄,成功
HSET user:1 city beijing    → 存入城市,成功

HGET user:1 name            → 只取姓名:zhangsan

HGETALL user:1              → 取出全部:
  name   → zhangsan
  age    → 20
  city   → beijing

HDEL user:1 city            → 删除 city 字段
HKEYS user:1                → 只看字段名:name  age
HVALS user:1                → 只看值:    zhangsan  20

使用场景: 存储对象信息(用户、商品、订单详情),比 String 存 JSON 更方便读写单个字段。


3. 下午小结

类型 特点 适合场景
String 一个 key 对应一个值 缓存、计数器、验证码
List 有序队列,两头操作 消息队列、浏览记录
Hash 一个 key 存多个字段 存对象信息(用户、商品)

七、Day 2 上午:Sorted Set + 重要机制

1. Sorted Set 有序集合

每个元素都有一个分数(score),Redis 会按分数自动排序,最适合做排行榜。

和 Set 的区别:

  • Set:无序,存进去是 {a, b, c},没有先后
  • Sorted Set:有序,每个元素带分数,{a:100, b:80, c:90},按分数排名

核心命令:

命令 作用
ZADD key score member 添加元素(设置分数)
ZREVRANGE key start stop 从高到低查看排名(排行榜用这个)
ZRANGE key start stop 从低到高查看排名
ZSCORE key member 查看某人分数
ZREVRANK key member 查看某人排名(从大到小,0=第1名)
ZRANK key member 查看某人排名(从小到大)

演示:

ini 复制代码
ZADD rank 95 zhangsan    → zhangsan 得分 95
ZADD rank 80 lisi        → lisi 得分 80
ZADD rank 100 wangwu     → wangwu 得分 100
ZADD rank 70 zhaoliu     → zhaoliu 得分 70

ZREVRANGE rank 0 -1 WITHSCORES   → 按分数从高到低:
  第1名:wangwu    100分
  第2名:zhangsan   95分
  第3名:lisi       80分
  第4名:zhaoliu    70分

ZSCORE rank zhangsan     → 分数:95
ZREVRANK rank zhangsan   → 排名第 2(从0开始算,0=第1名)

ZADD rank 120 lisi       → 更新 lisi 分数为 120
ZREVRANGE 0 -1           → lisi 马上变成第1名

排名从 0 开始:0=第1名,1=第2名,2=第3名

使用场景: 排行榜、热搜榜、积分排名


2. 过期策略

Redis 可以给任意 key 设置过期时间,时间到了自动删除。

命令 作用
EXPIRE key 秒数 给已存在的 key 设置过期时间
TTL key 查看剩余时间(正数=秒数,-1=永不过期,-2=已删除)
PERSIST key 取消过期,变成永久

为什么缓存要用过期时间?防止缓存永远存在,数据改了但缓存没更新。


3. 持久化

Redis 数据在内存里,服务器重启就没了。持久化就是把内存数据定期保存到硬盘。

方式 原理 特点
RDB 定期拍快照存到硬盘 恢复快,可能丢失最后几秒数据
AOF 把每条写命令都记录下来 数据更安全,文件更大、恢复更慢

了解即可,实际项目用默认配置就够。


4. 事务

把多条命令打包一起执行,中间不会被其他命令打断:

sql 复制代码
MULTI         → 开始事务
SET a 1       → 命令入队
SET b 2       → 命令入队
EXEC          → 一起执行

了解即可,实际项目用得少。


5. 发布订阅(Pub/Sub)

类似广播电台:有人发消息,所有订阅者都能收到。

复制代码
SUBSCRIBE news       → 订阅 news 频道
PUBLISH news hello   → 发布消息,所有订阅者收到

使用场景:实时通知、消息推送、解耦系统间通信


6. 上午小结

  1. Sorted Set 带分数排序,排行榜首选
  2. 过期策略 用 EXPIRE/TTL 控制缓存存活时间
  3. 持久化 RDB(快照)和 AOF(日志)两种方式
  4. 发布订阅 用于广播消息给多个订阅者

八、Day 2 下午:Spring Boot 集成 Redis

1. 项目配置

pom.xml 依赖:

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

application-dev.yml 配置:

yaml 复制代码
redis:
  host: localhost      # Redis 服务器地址
  port: 6379           # 端口
  password: 123456     # 密码
  database: 10         # 用第10号库(0-15共16个库)

RedisConfiguration.java --- 把 key 序列化改成可读的字符串:

java 复制代码
redisTemplate.setKeySerializer(new StringRedisSerializer());

没有这行,Redis 里存的 key 会是乱码字节,加了之后才是可读的 dish_1


2. RedisTemplate 五种类型操作

注入方式:

java 复制代码
@Autowired
private RedisTemplate redisTemplate;

String 类型(项目里用得最多):

Java 代码 等价的 Redis 命令
redisTemplate.opsForValue().set(k, v) SET k v
redisTemplate.opsForValue().get(k) GET k
redisTemplate.opsForValue().set(k, v, 30, TimeUnit.MINUTES) SETEX k 1800 v
redisTemplate.delete(k) DEL k
redisTemplate.keys("dish_*") KEYS dish_*
redisTemplate.hasKey(k) EXISTS k

Hash 类型:

Java 代码 等价的 Redis 命令
redisTemplate.opsForHash().put(k, field, value) HSET k field value
redisTemplate.opsForHash().get(k, field) HGET k field
redisTemplate.opsForHash().entries(k) HGETALL k
redisTemplate.opsForHash().delete(k, field) HDEL k field

List 类型:

Java 代码 等价的 Redis 命令
redisTemplate.opsForList().leftPush(k, v) LPUSH k v
redisTemplate.opsForList().rightPush(k, v) RPUSH k v
redisTemplate.opsForList().leftPop(k) LPOP k
redisTemplate.opsForList().range(k, 0, -1) LRANGE k 0 -1

Set 类型:

Java 代码 等价的 Redis 命令
redisTemplate.opsForSet().add(k, v) SADD k v
redisTemplate.opsForSet().members(k) SMEMBERS k
redisTemplate.opsForSet().isMember(k, v) SISMEMBER k v

Sorted Set 类型:

Java 代码 等价的 Redis 命令
redisTemplate.opsForZSet().add(k, member, score) ZADD k score member
redisTemplate.opsForZSet().reverseRange(k, 0, -1) ZREVRANGE k 0 -1
redisTemplate.opsForZSet().score(k, member) ZSCORE k member

3. 项目实战:菜品缓存流程

读(user/DishController.java):

java 复制代码
// 第一步:构造 key
String key = "dish_" + categoryId;

// 第二步:先查 Redis 缓存
List<DishVO> list = (List<DishVO>) redisTemplate.opsForValue().get(key);
if (list != null) {
    return Result.success(list);   // 命中,直接返回,不查数据库
}

// 第三步:缓存没有,查数据库
list = dishService.listWithFlavor(dish);

// 第四步:查出来后存进 Redis
redisTemplate.opsForValue().set(key, list);
return Result.success(list);

写(admin/DishController.java):

java 复制代码
// 新增/修改/删除菜品时,把缓存清掉
// 这样下次查询会重新从数据库读,保证数据一致
private void cleanCache(String pattern) {
    Set keys = redisTemplate.keys(pattern);
    redisTemplate.delete(keys);
}

cleanCache("dish_" + categoryId);   // 清该分类缓存
cleanCache("dish_*");               // 清全部菜品缓存

4. 加过期时间的写法

java 复制代码
// 写入缓存,30分钟后自动过期
redisTemplate.opsForValue().set(key, list, 30, TimeUnit.MINUTES);

加上过期时间,即使忘记清理缓存,数据也不会永远过时。


5. 两天学习总结

类型 命令 Java 方法 使用场景
String SET/GET/SETEX opsForValue() 缓存、验证码、计数器
List LPUSH/RPUSH/LRANGE opsForList() 消息队列、浏览记录
Hash HSET/HGET/HGETALL opsForHash() 存对象信息
Set SADD/SMEMBERS opsForSet() 标签、去重
Sorted Set ZADD/ZREVRANGE opsForZSet() 排行榜

相信你通过这个学习已经能够理解redis的作用以及能够应用了

相关推荐
用户2930750976691 小时前
从零搭建 AI 日记助手:Milvus 向量数据库 + RAG 实战
后端
玉宇夕落1 小时前
Milvus向量数据库和cs bs架构学习
后端
Larcher1 小时前
别再搞混了!一文讲透 AI Workflow 和 Agent 的本质区别
javascript·人工智能·后端
瀛川1 小时前
microVM 技术解析:面向多租户 Serverless 与 AI Agent 的微虚拟机架构
后端·云原生
Conan在掘金2 小时前
鸿蒙报错速查:装对象字量禁 as Object 编译炸,根因 + 真解法
后端
IT_陈寒2 小时前
Python的finally居然不等同于Go的defer,差点坑惨我
前端·人工智能·后端
会编程的土豆2 小时前
功能详解 01 · 用户注册:从表单到数据库一行
开发语言·数据库·后端·mysql·golang
Ivanqhz2 小时前
Rust collect() 浅析
开发语言·后端·rust
卷无止境2 小时前
python进阶:类方法与静态方法的分野,classmethod 和 staticmethod 到底该怎么选
后端·python