Redis详解_数据结构_缓存_SpringBoot_分布式锁

O泡李华 8

Redis 详解:数据结构、缓存、SpringBoot、分布式锁与高并发实战

本章位置:第三阶段 Java 企业项目 + AI 助手
对应课程:Redis
前置知识:Java 集合、MySQL、SpringBoot、Maven、HTTP、事务、Git
后续衔接:若依脚手架、淘车湾项目、高并发缓存、微服务
学习目标:从零掌握 Redis 的定位、五大核心数据类型、TTL、持久化、缓存设计、缓存穿透/击穿/雪崩、双写一致性、SpringBoot 集成、Token、验证码、排行榜、分布式锁、高可用与常见排错。


一、Redis 是什么

Redis 是一个高性能、基于内存、支持多种数据结构并可持久化的 Key-Value 数据库。

最常见用途:

热点缓存
登录 Token
验证码
Session
计数器
排行榜
点赞/关注
分布式锁
限流
幂等
简单消息流

Redis 不能简单替代 MySQL。企业项目最典型的组合是:

MySQL
负责长期、结构化、可靠的业务数据

Redis
负责高性能访问、缓存和分布式协调

典型查询链路:

Browser
↓
SpringBoot
↓
Redis
├─ 命中 → 直接返回
└─ 未命中
   ↓
   MySQL
   ↓
   回填 Redis

二、Redis 为什么快

最重要的原因包括:

数据主要在内存
高效数据结构
事件驱动网络模型
命令逻辑较轻量
减少磁盘随机访问

不要死记:

Redis 快 = 因为单线程

更准确的理解是:Redis 的核心命令处理长期采用串行化执行模型,但现代 Redis 仍有后台线程、I/O 等机制,所以不能理解成“整个 Redis 进程只有一个线程”。


三、Windows 上如何运行 Redis

Windows 学习环境推荐:

Docker
WSL
Windows 兼容实现

如果已经有 Docker,最简单:

docker run -d --name redis -p 6379:6379 redis

查看:

docker ps

进入 Redis CLI:

docker exec -it redis redis-cli

测试:

PING

返回:

PONG

说明 Redis 正常。

默认端口:

6379

四、Redis Key 基础

查看 Key 是否存在:

EXISTS user:1

删除:

DEL user:1

查看类型:

TYPE user:1

学习环境查看所有 Key:

KEYS *

生产环境不推荐随便使用 KEYS *,Key 很多时可能带来明显性能影响。更推荐:

SCAN 0 MATCH user:* COUNT 100

Key 命名建议:

业务:模块:标识

例如:

user:detail:1001
login:token:abc123
sms:code:13800000000
rank:answer:2026:09
lock:order:1001

五、TTL

TTL:

Time To Live

表示 Key 剩余存活时间。

设置:

EXPIRE user:1 60

查看:

TTL user:1

毫秒:

PTTL user:1

取消过期:

PERSIST user:1

更推荐写入时直接设置:

SET login:code:13800000000 123456 EX 300

相比:

SET key value
EXPIRE key 300

单条 SET ... EX 更好,因为“写值 + 设置过期”在一条命令中完成,避免两条命令之间程序异常导致 Key 永不过期。

过期删除主要可以理解为:

访问时发现过期 → 惰性删除
+
Redis 周期检查过期 Key → 主动删除

六、String

String 是最常用的数据类型,可以保存:

字符串
整数
浮点数字符串
JSON
二进制数据

基本命令:

SET name zhangsan
GET name

MSET name zhangsan age 20 city shenyang
MGET name age city

计数器:

SET count 0
INCR count
INCRBY count 10
DECR count

INCR 适合高并发计数,因为它本身是 Redis 原子命令。

Java 中不要这样:

GET
↓
Java count++
↓
SET

因为多个请求并发时可能丢更新。

场景:

缓存
计数器
Token
验证码
JSON 对象

七、Hash

Hash 可以类比:

Map<String, String>

例如:

user:1001
├─ name → 张三
├─ age → 20
└─ city → 沈阳

命令:

HSET user:1001 name 张三 age 20 city 沈阳
HGET user:1001 name
HMGET user:1001 name age
HGETALL user:1001
HDEL user:1001 city
HEXISTS user:1001 name
HINCRBY user:1001 score 10

适合:

对象字段
购物车
简单配置

String JSON 与 Hash 怎么选:

经常整体读写对象
→ String + JSON

经常单独修改字段
→ Hash

不要死记“Hash 一定更省内存”,实际要看对象大小、字段数量、访问方式和 Redis 版本。


八、List

List:

有序
允许重复
支持两端插入/弹出

命令:

LPUSH queue a
RPUSH queue b
LPOP queue
RPOP queue
LRANGE queue 0 -1
LLEN queue

可以实现简单队列:

生产:RPUSH
消费:LPOP

也可以使用阻塞命令:

BLPOP queue 30
BRPOP queue 30

但 List 不等于专业 MQ。关键业务消息通常还需要:

消费确认
重试
死信
消费组
更完整持久化语义

复杂业务更适合 RabbitMQ / RocketMQ / Kafka 等。


九、Set

Set:

无序
元素不重复

命令:

SADD user:1001:tags Java Spring Redis
SMEMBERS user:1001:tags
SISMEMBER user:1001:tags Java
SREM user:1001:tags Redis
SCARD user:1001:tags

集合运算:

SINTER a b
SUNION a b
SDIFF a b

适合:

点赞
标签
关注关系
共同关注
去重

点赞设计:

SADD article:1001:likes user:2001
SISMEMBER article:1001:likes user:2001
SCARD article:1001:likes

十、ZSet

Sorted Set / ZSet:

Member 不重复
每个 Member 有 Score
按 Score 排序

命令:

ZADD rank 100 user1
ZADD rank 80 user2
ZADD rank 120 user3
ZINCRBY rank 10 user1
ZSCORE rank user1

排行榜:

ZREVRANGE rank 0 9 WITHSCORES

查询排名:

ZREVRANK rank user1

Redis 排名从 0 开始,页面通常显示:

rank + 1

场景:

排行榜
热度榜
积分榜
按权重排序

月榜可以设计:

rank:answer:2026:09

周榜:

rank:answer:2026:week:37

十一、其他数据类型

Bitmap:

签到
在线状态
布尔状态统计

HyperLogLog:

UV 等近似基数统计

Geo:

附近门店
经纬度搜索

Stream:

消息流
消费组
Pending

当前课程优先掌握:

String
Hash
List
Set
ZSet

十二、Redis 原子性、事务、Lua、Pipeline

很多单条 Redis 命令是原子的,例如:

INCR
HINCRBY
SET ... NX

Redis 事务核心命令:

MULTI
EXEC
DISCARD
WATCH

注意:

Redis Transaction
不等于 MySQL Transaction

不要想当然认为任何执行错误都会像 MySQL 一样自动整体回滚。

WATCH 可以理解为:

乐观锁

如果执行 EXEC 前被监视的 Key 已被别人修改,事务可以放弃。

Lua 脚本适合把多条 Redis 操作在服务端一次执行,常见场景:

分布式锁解锁
库存判断 + 扣减
限流
幂等校验

Pipeline:

主要减少客户端与 Redis 的网络往返 RTT

它不是事务。


十三、持久化:RDB 和 AOF

Redis 数据主要在内存,因此需要考虑重启恢复。

RDB:

定期生成数据快照

优点:

文件紧凑
恢复速度通常较快
适合备份

缺点:

快照之间可能丢失最近一段数据

AOF:

记录写操作
重启时重放

优点:

可获得更小的数据丢失窗口

缺点:

文件更大
写入和重写有额外开销

企业系统根据:

性能
恢复目标
数据重要性

选择 RDB、AOF 或组合。

如果 Redis 只是缓存,最好设计成:

Redis 数据丢失后
仍能从 MySQL 重建

十四、缓存是什么

缓存的目标:

降低数据库压力
降低接口响应时间

最常见 Cache Aside:

查询 Redis
├─ 命中 → 返回
└─ 未命中
   ↓
   查 MySQL
   ↓
   写 Redis
   ↓
   返回

伪代码:

public UserVO getUser(Long id) {

    String key =
            "user:detail:"
            + id;

    UserVO cache =
            userCacheService.get(
                    key
            );

    if (cache != null) {
        return cache;
    }

    User user =
            userMapper.selectById(
                    id
            );

    if (user == null) {
        return null;
    }

    UserVO result =
            convert(user);

    userCacheService.set(
            key,
            result
    );

    return result;
}

十五、缓存穿透

缓存穿透:

请求数据库根本不存在的数据

例如一直请求:

userId=-1
userId=999999999

每次:

Redis Miss
↓
MySQL 也没有

数据库仍不断被访问。

解决:

参数校验
缓存空值
Bloom Filter

缓存空值示例:

user:detail:999
→ 特殊 NULL 标记
TTL 2 分钟

空值 TTL 通常较短,避免新数据创建后仍长时间被旧空缓存挡住。

Bloom Filter:

判断结果:
一定不存在
或
可能存在

存在一定误判率。


十六、缓存击穿

缓存击穿:

一个极热点 Key 突然失效

大量请求同时:

Redis Miss
↓
MySQL

例如:

爆款商品详情
首页热门配置

常见解决:

互斥重建
逻辑过期
热点预热

互斥重建:

Cache Miss
↓
抢锁
├─ 成功
│  ↓
│ 查 DB
│  ↓
│ 写 Cache
│  ↓
│ 解锁
└─ 失败
   ↓
   短暂等待
   ↓
   再读 Cache

逻辑过期:

Redis Key 不真正立即删除
Value 内保存 expireTime

过期后:

当前请求可暂时返回旧值
后台线程重建

适合:

允许短暂旧数据的极热点场景

不适合:

余额
强权限变更
强一致库存

十七、缓存雪崩

缓存雪崩:

大量 Key 同时失效
或 Redis 整体不可用

导致:

大量请求打到数据库

解决:

TTL 加随机值
缓存预热
Redis 高可用
限流
熔断
降级

例如:

30 分钟
+
随机 0~5 分钟

让过期时间打散。

三大问题记忆:

穿透
→ 不存在的数据

击穿
→ 一个热点 Key

雪崩
→ 大量 Key / Redis 整体

十八、Hot Key 与 Big Key

Hot Key:

访问量远高于其他 Key

风险:

单节点 CPU / 网络热点
失效时容易击穿

Big Key:

一个 Key 的 Value 特别大
或集合元素特别多

例如:

几十 MB String
百万字段 Hash
百万元素 List

风险:

网络传输慢
删除耗时
内存分布不均
阻塞时间增加
复制压力增大

不要:

HGETALL 超大 Hash
LRANGE huge-list 0 -1
SMEMBERS 超大 Set

需要考虑:

分页
SCAN / HSCAN / SSCAN / ZSCAN
重新拆 Key

十九、缓存一致性

假设:

MySQL User
+
Redis User Cache

用户修改昵称。

常见 Cache Aside 写策略:

先更新数据库
再删除缓存

为什么不是直接更新缓存?

因为并发下可能出现:

旧值覆盖新值

删除缓存后:

下一次读取 Cache Miss
↓
查最新 DB
↓
重建 Cache

但要理解:

数据库 + Redis
不是天然强一致事务

仍存在并发和失败窗口。

常见增强:

TTL 兜底
消息重试
延迟双删
Canal 监听 Binlog
版本号

不能只背:

“更新 DB 再删 Redis 就百分百一致”

二十、Spring @Transactional 与 Redis

不要以为:

@Transactional

可以让:

MySQL + Redis

自动成为一个强一致分布式事务。

数据库和 Redis 是:

两个独立资源系统

尤其注意:

数据库事务还没提交
就提前写 Redis

如果数据库最终回滚:

Redis 不会自动跟着回滚业务缓存

因此缓存更新要认真考虑:

事务提交时机

二十一、SpringBoot 集成 Redis

Maven:

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

现代 Spring Boot 配置:

spring:
  data:
    redis:
      host: localhost
      port: 6379
      database: 0

有密码:

spring:
  data:
    redis:
      host: localhost
      port: 6379
      database: 0
      password: your_password

旧教程可能看到:

spring.redis.*

新项目应优先检查当前 Spring Boot 官方配置;现代版本常用:

spring.data.redis.*

Spring Boot 的 Redis Starter 默认通常使用:

Lettuce

也支持:

Jedis

学习阶段保持默认 Lettuce 即可。


二十二、RedisTemplate 与 StringRedisTemplate

Spring Data Redis 核心:

RedisTemplate<K,V>
StringRedisTemplate

StringRedisTemplate:

Key / Value 都按 String 处理
默认字符串序列化
CLI 中更容易读

初学非常推荐。

注入:

@Service
public class RedisDemoService {

    private final StringRedisTemplate
            redisTemplate;

    public RedisDemoService(
            StringRedisTemplate redisTemplate
    ) {
        this.redisTemplate =
                redisTemplate;
    }
}

String:

redisTemplate
        .opsForValue()
        .set(
                "name",
                "zhangsan"
        );

读取:

String value =
        redisTemplate
                .opsForValue()
                .get(
                        "name"
                );

TTL:

redisTemplate
        .opsForValue()
        .set(
                "login:code:13800000000",
                "123456",
                Duration.ofMinutes(5)
        );

二十三、Spring Data Redis 操作对应

String:

redisTemplate.opsForValue()

Hash:

redisTemplate.opsForHash()

List:

redisTemplate.opsForList()

Set:

redisTemplate.opsForSet()

ZSet:

redisTemplate.opsForZSet()

例如 Hash:

redisTemplate
        .opsForHash()
        .put(
                "user:1001",
                "name",
                "张三"
        );

Set:

redisTemplate
        .opsForSet()
        .add(
                "article:1001:likes",
                "user:1"
        );

ZSet:

redisTemplate
        .opsForZSet()
        .incrementScore(
                "user:rank",
                "user:1",
                10
        );

二十四、Redis 序列化

Redis 实际存的是:

字节

Java Object 需要:

序列化

Spring Data Redis 的通用 RedisTemplate 默认配置可能使用 Java 原生序列化,CLI 中可能看到:

二进制乱码样式

而且 Java 原生反序列化对不可信数据存在安全风险。

常见工程方案:

Key
→ StringRedisSerializer

Value
→ JSON

学习阶段最容易理解的方式:

ObjectMapper
对象 → JSON String
↓
StringRedisTemplate

写入:

String json =
        objectMapper
                .writeValueAsString(
                        userVO
                );

redisTemplate
        .opsForValue()
        .set(
                key,
                json,
                Duration.ofMinutes(
                        30
                )
        );

读取:

String json =
        redisTemplate
                .opsForValue()
                .get(
                        key
                );

UserVO result =
        objectMapper
                .readValue(
                        json,
                        UserVO.class
                );

二十五、缓存 Key 设计

推荐集中管理:

public final class RedisKeys {

    private RedisKeys() {
    }

    public static String userDetail(
            Long userId
    ) {

        return "user:detail:"
                + userId;
    }

    public static String loginToken(
            String token
    ) {

        return "login:token:"
                + token;
    }
}

不要在 Controller / Service 到处:

手写字符串

企业缓存设计最好明确:

Key
Value Type
TTL
数据来源
什么时候写
什么时候删
是否允许旧数据
Redis 故障如何处理

二十六、用户详情缓存实战

查询:

GET /users/{id}
↓
Redis
├─ Hit → 返回
└─ Miss
   ↓
   MySQL
   ├─ 不存在 → 缓存空值
   └─ 存在 → JSON + TTL

更新:

@Transactional
UPDATE MySQL
↓
事务成功
↓
DELETE user:detail:{id}

下次查询:

重新回填最新缓存

二十七、Spring Cache

Spring 提供缓存抽象:

@Cacheable
@CacheEvict
@CachePut

例如:

@Cacheable(
        cacheNames = "user",
        key = "#id"
)
public UserVO getUser(
        Long id
) {
    ...
}

更新后删除:

@CacheEvict(
        cacheNames = "user",
        key = "#id"
)
public void updateUser(
        Long id,
        UserUpdateDTO dto
) {
    ...
}

优点:

代码简洁

缺点:

复杂缓存击穿
逻辑过期
自定义锁
复杂失效

时不一定够灵活。

学习顺序:

先理解 RedisTemplate
再学缓存注解

二十八、Redis Token 登录

一种典型登录方案:

用户名密码
↓
MySQL 校验
↓
生成随机 Token
↓
Redis:
login:token:{token}
→ UserSession JSON
↓
TTL 30 分钟
↓
返回 Token

请求:

Authorization: Bearer token
↓
Interceptor / Filter
↓
Redis GET
↓
有 Session → 登录有效
无 → 401

退出:

DEL login:token:{token}

滑动过期:

每次有效访问
适当刷新 TTL

高并发系统可避免每个请求都刷新,减少额外写流量。


二十九、Redis 验证码

Key:

login:code:{phone}

Value:

123456

TTL:

5 分钟

发送频率限制:

sms:send:limit:{phone}

通过:

SET NX EX 60

做到:

60 秒内只能发送一次

注意:

前端倒计时不是安全限制

用户可以绕过前端直接调用 API,所以真正限制必须在后端。

验证码校验成功后建议:

立即 DEL

做到一次性使用。


三十、ZSet 排行榜实战

积分增加:

redisTemplate
        .opsForZSet()
        .incrementScore(
                "rank:answer:2026:09",
                String.valueOf(
                        userId
                ),
                10
        );

Top 10:

redisTemplate
        .opsForZSet()
        .reverseRangeWithScores(
                "rank:answer:2026:09",
                0,
                9
        );

当前用户排名:

Long rank =
        redisTemplate
                .opsForZSet()
                .reverseRank(
                        "rank:answer:2026:09",
                        String.valueOf(
                                userId
                        )
                );

页面:

rank + 1

重要业务最好:

MySQL 保存积分记录
Redis 提供排行榜

三十一、分布式锁为什么需要 Redis

单机:

synchronized
ReentrantLock

只能控制:

一个 JVM

多实例:

Server A
Server B
Server C

每台 JVM:

都有自己的本地锁

所以需要:

共享的锁服务

Redis 就可以作为一种实现。


三十二、错误的 Redis 锁

错误方案:

SETNX lock:key 1

问题:

应用崩溃
锁永远存在

继续改:

SETNX lock:key 1
EXPIRE lock:key 30

仍有问题:

两条命令

如果 SETNX 成功后程序崩溃:

EXPIRE 没执行

仍可能死锁。


三十三、基础正确加锁

使用一条命令:

SET lock:order:1001 uuid-A NX EX 30

含义:

Key 不存在才设置
+
30 秒过期
+
Value 是当前客户端唯一标识

为什么 Value 不能固定为 1?

场景:

A 获得锁
↓
A 执行业务超过 30 秒
↓
锁过期
↓
B 获得新锁
↓
A 执行结束
↓
A DEL

如果 Value 一样:

A 可能误删 B 的锁

三十四、为什么解锁必须原子

错误 Java:

GET lock
↓
判断 value 是自己的
↓
DEL lock

GET 和 DEL 之间:

锁可能过期
另一个客户端获得新锁

然后旧客户端:

误删新锁

所以要用 Lua:

if redis.call('get', KEYS[1]) == ARGV[1] then
    return redis.call('del', KEYS[1])
else
    return 0
end

把:

比较
+
删除

作为一个原子操作。


三十五、锁续期与 Watchdog

如果正常业务时间:

不确定

固定 TTL 可能:

业务还没执行完
锁先过期

成熟客户端会考虑:

自动续期

例如 Redisson:

Watchdog

在持锁线程正常工作时:

自动延长锁 TTL

三十六、Redisson

Redisson 是常用 Redis Java 客户端,提供:

RLock
RMap
RSet
Semaphore

分布式锁示意:

RLock lock =
        redissonClient
                .getLock(
                        "lock:order:"
                        + orderId
                );

boolean locked =
        lock.tryLock();

try {

    if (!locked) {
        throw new BusinessException(
                "操作处理中"
        );
    }

    // business

} finally {

    if (
            locked
            &&
            lock.isHeldByCurrentThread()
    ) {

        lock.unlock();
    }
}

真实项目优先成熟库,不要每个项目复制一份自己实现的 RedisLockUtil。


三十七、分布式锁不是万能保证

非常关键的业务仍应考虑:

数据库唯一约束
幂等
事务
锁过期
网络分区
故障转移
Fencing Token

例如:

一个用户只能领取一张券

数据库仍可以有:

UNIQUE(user_id, coupon_id)

作为最后一道防线。


三十八、Redis 官方锁模型要注意什么

简单单节点锁:

SET NX EX
+
唯一 Value
+
原子解锁

适合很多普通业务协调。

但在主从故障切换、网络分区、长时间任务等情况下,要认真考虑:

锁的安全性

Redis 官方还讨论 Redlock 和 fencing token 等问题。

因此:

不能把“Redis 锁”理解成绝对不会出错

三十九、限流

简单固定窗口:

Key:
rate:user:1001:202609101900

每请求:

INCR

第一次设置:

60 秒 TTL

超过:

100

拒绝。

缺点:

窗口边界可能瞬间双倍

更高级:

滑动窗口
令牌桶
漏桶

可以结合 Redis + Lua。


四十、幂等

用户连续点击:

提交订单

可能产生:

重复请求

一种方案:

生成一次性幂等 Token
↓
Redis 保存
↓
提交时 Lua 原子校验 + 删除

不能:

GET
↓
DEL

分两步,否则并发不安全。


四十一、Pub/Sub 与 Stream

Pub/Sub:

实时广播

如果消费者不在线:

消息可能直接错过

适合:

轻量通知

不适合:

必须可靠消费的关键业务

Redis Streams:

消息 ID
消费组
Pending

能力更完整,但复杂业务仍应根据需求和 RabbitMQ / RocketMQ / Kafka 比较。


四十二、主从、Sentinel、Cluster

Replication:

Master
↓
Replica

作用:

数据副本
读扩展
高可用基础

复制通常存在:

异步延迟

Sentinel:

监控主从
故障检测
自动 Failover
发现新主节点

Cluster:

数据分片
+
高可用

Key 会映射到:

Hash Slot

再分配到多个节点。

学习顺序:

单机
↓
主从
↓
Sentinel
↓
Cluster

四十三、Redis 安全

生产不要:

6379 直接暴露公网

应考虑:

内网
防火墙
ACL
密码/用户
TLS
最小权限

不要把生产 Redis 密码提交到 Git。

危险命令:

FLUSHALL
FLUSHDB
KEYS *
CONFIG
DEBUG

都需要谨慎。


四十四、Redis 监控

常关注:

内存
命中率
连接数
OPS
Slowlog
Evicted Keys
复制延迟
Hot Key
Big Key

查看:

INFO
INFO memory
INFO stats

慢日志:

SLOWLOG GET

Redis 不是所有命令都是 O(1)。

例如:

GET
通常近 O(1)

HGET
通常近 O(1)

HGETALL
O(N)

SMEMBERS
O(N)

N 很大时仍可能慢。


四十五、常见报错排查

1. Connection refused

检查:

Redis 是否启动
host
port
Docker 端口映射
password
防火墙

2. Docker 里 SpringBoot 连接 localhost 失败

如果 SpringBoot 也在容器中:

localhost

指:

当前 SpringBoot 容器自己

不是 Redis 容器。

Docker Compose 服务之间应考虑:

redis:6379

3. Redis CLI 有 Key,SpringBoot 查不到

检查:

database index
Redis 地址
Key 前缀
序列化
环境

4. CLI 看到乱码

常见:

JDK Serialization

使用:

StringRedisTemplate
或 String Key + JSON Value

5. 缓存命中但数据旧

检查:

更新 DB 后是否删除缓存
TTL
事务提交时机
不同环境 Key

6. 内存一直涨

检查:

TTL
Key 数量
Big Key
历史排行榜
缓存是否只增不删

四十六、Redis Key 设计文档示例

名称:
用户详情缓存

Key:
user:detail:{userId}

Value:
UserVO JSON

TTL:
30~35 分钟随机

数据源:
MySQL sys_user

读取:
Cache Aside

回填:
Cache Miss 后查 DB

失效:
用户更新成功后删除

不存在数据:
空值缓存 2 分钟

一致性:
最终一致

故障:
Redis 异常时按限流策略降级 MySQL

真正做企业缓存设计时,不能只写:

“使用 Redis 优化性能”

而应明确:

缓存什么
为什么缓存
Key 是什么
TTL 多久
什么时候删
Redis 挂了怎么办

四十七、Redis 与前面知识的关系

Redis + SpringBoot:

starter
↓
AutoConfiguration
↓
RedisConnectionFactory
↓
RedisTemplate

Redis + MySQL:

Redis Cache
↓ Miss
MySQL

Redis + Maven:

spring-boot-starter-data-redis
↓
Spring Data Redis
↓
Lettuce

Redis + 事务:

MySQL Transaction
≠
Redis Transaction

Redis + 多实例:

synchronized
只能 JVM 内

Redis Lock
可做跨实例协调

Redis + Vue:

Vue
不能直接访问 Redis

Vue
↓
SpringBoot API
↓
Redis

四十八、练习清单

  1. Docker 启动 Redis,执行 PING。
  2. 使用 String 完成 SET/GET/INCR/EXPIRE/TTL。
  3. 使用 Hash 保存用户字段。
  4. 使用 List 实现简单队列。
  5. 使用 Set 实现点赞和去重。
  6. 使用 ZSet 实现 Top 10 排行榜。
  7. 创建 SpringBoot 项目并引入 spring-boot-starter-data-redis。
  8. 使用 StringRedisTemplate 完成 String/Hash/Set/ZSet 操作。
  9. 用 ObjectMapper + JSON 缓存 UserVO。
  10. 实现用户详情 Cache Aside。
  11. 为不存在用户增加空值缓存。
  12. 修改用户后删除缓存。
  13. 实现 Redis Token 登录状态。
  14. 实现验证码 5 分钟过期。
  15. 用 SET NX EX 实现发送验证码 60 秒限频。
  16. 用 ZSet 实现月榜。
  17. 使用 SET NX EX + UUID + Lua 实现基础分布式锁。
  18. 模拟热点 Key 缓存击穿。
  19. 用互斥锁实现缓存重建。
  20. 给批量缓存增加随机 TTL。
  21. 使用 SCAN 查找指定前缀 Key。
  22. 使用 SLOWLOG GET 理解慢命令。
  23. 体验 Pipeline 与普通循环的网络差异。
  24. 学习 RDB/AOF 配置与重启恢复。
  25. 用 Redis Insight 辅助查看数据,但同时坚持会用 redis-cli。

四十九、常见面试题

1. Redis 为什么快?

数据主要在内存,
使用高效数据结构和事件驱动网络模型,
很多命令实现较轻量。
不能只回答“因为单线程”。

2. Redis 五大基本类型?

String
Hash
List
Set
ZSet

3. 穿透、击穿、雪崩区别?

穿透:
查不存在的数据

击穿:
一个热点 Key 失效

雪崩:
大量 Key 或 Redis 大面积失效

4. 为什么更新 DB 后删除缓存?

Cache Aside 常见写策略。
删除缓存后下一次读取重新从 DB 加载,
比直接并发更新 DB 和缓存更容易控制一致性。

5. 为什么不能保证绝对一致?

MySQL 和 Redis 是两个独立系统,
存在并发、失败和事务时序窗口。

6. Redis 分布式锁基本方案?

SET key uniqueValue NX EX ttl
+
Lua compare-and-delete

7. 为什么不能 SETNX 再 EXPIRE?

两条命令之间可能崩溃,
导致锁永久存在。

8. 为什么解锁不能 GET 后 DEL?

两条命令不原子,
可能误删别人刚获得的新锁。

9. RDB 和 AOF?

RDB:快照
AOF:写命令日志

10. RedisTemplate 与 StringRedisTemplate?

RedisTemplate 更通用,
StringRedisTemplate 专门针对 String,
默认字符串序列化,更易调试。

11. 为什么生产不建议 KEYS *?

Key 多时一次扫描可能影响 Redis 响应,
推荐使用 SCAN。

12. ZSet 为什么适合排行榜?

Member 唯一,
每个 Member 有 Score,
Redis 按 Score 维护排序。

13. Set 为什么适合点赞?

Member 不重复,
天然可以用 userId 去重。

14. Pipeline 与事务区别?

Pipeline 主要优化网络 RTT,
Transaction 提供命令批量顺序执行语义。

15. Sentinel 与 Cluster?

Sentinel 更偏主从高可用和 Failover;
Cluster 同时解决分片容量扩展和高可用。

五十、必须掌握的命令

Key:
EXISTS DEL TYPE EXPIRE TTL PTTL PERSIST SCAN

String:
SET GET MSET MGET INCR DECR INCRBY

Hash:
HSET HGET HMGET HGETALL HDEL HEXISTS HINCRBY

List:
LPUSH RPUSH LPOP RPOP LRANGE LLEN BLPOP BRPOP

Set:
SADD SREM SMEMBERS SISMEMBER SCARD SINTER SUNION SDIFF

ZSet:
ZADD ZINCRBY ZRANGE ZREVRANGE ZRANK ZREVRANK ZSCORE

Transaction:
MULTI EXEC DISCARD WATCH

Monitoring:
INFO SLOWLOG

五十一、必须掌握的 Spring Data Redis

spring-boot-starter-data-redis
spring.data.redis.*
RedisConnectionFactory
RedisTemplate
StringRedisTemplate

opsForValue()
opsForHash()
opsForList()
opsForSet()
opsForZSet()

五十二、Redis 核心知识树

Redis
│
├─ Data Types
│  ├─ String
│  ├─ Hash
│  ├─ List
│  ├─ Set
│  └─ ZSet
│
├─ Key
│  ├─ Naming
│  ├─ TTL
│  ├─ SCAN
│  ├─ Hot Key
│  └─ Big Key
│
├─ Persistence
│  ├─ RDB
│  └─ AOF
│
├─ Atomic
│  ├─ Atomic Command
│  ├─ MULTI/EXEC
│  ├─ WATCH
│  ├─ Lua
│  └─ Pipeline
│
├─ Cache
│  ├─ Cache Aside
│  ├─ Penetration
│  ├─ Breakdown
│  ├─ Avalanche
│  ├─ Warm-up
│  └─ Consistency
│
├─ SpringBoot
│  ├─ Lettuce
│  ├─ RedisTemplate
│  ├─ StringRedisTemplate
│  └─ Serialization
│
├─ Business
│  ├─ Token
│  ├─ Verify Code
│  ├─ Ranking
│  ├─ Like
│  ├─ Rate Limit
│  └─ Idempotency
│
├─ Lock
│  ├─ SET NX EX
│  ├─ Unique Value
│  ├─ Lua Unlock
│  ├─ Watchdog
│  └─ Redisson
│
└─ High Availability
   ├─ Replication
   ├─ Sentinel
   └─ Cluster

五十三、本章最终总结

Redis 最核心的五大类型:

String
Hash
List
Set
ZSet

形成条件反射:

普通缓存 / Token / 计数
→ String

对象字段
→ Hash

队列
→ List / Stream

去重
→ Set

排行榜
→ ZSet

缓存最基本链路:

Redis
↓ Miss
MySQL
↓
Redis

三大缓存问题:

穿透
→ 不存在数据

击穿
→ 一个热点 Key

雪崩
→ 大量 Key / Redis 整体

缓存一致性常见思路:

更新数据库
↓
删除缓存
↓
TTL 最终兜底

SpringBoot:

spring-boot-starter-data-redis
spring.data.redis.*
StringRedisTemplate
RedisTemplate

分布式锁:

SET NX EX
+
唯一 Value
+
Lua 解锁
+
TTL
+
续期
+
数据库约束

真正会 Redis,不是只会:

redisTemplate
    .opsForValue()
    .set(...)

而是知道:

为什么缓存
缓存什么
TTL 多久
什么时候失效
Redis 挂了怎么办
怎么防穿透/击穿/雪崩
怎么保证业务最终一致
为什么分布式锁可能失效
怎么避免 Big Key / Hot Key

到这里,第三阶段的:

Redis

课程单元完成。

按照课程表,下一篇进入:

使用 AI 了解若依脚手架
第一部分:POI + RBAC

后面会把前面学过的:

SpringBoot
Vue
MyBatis
Redis
RBAC
Git

真正放进若依项目中串起来。


官方学习参考

Redis 官方文档:

https://redis.io/docs/latest/

Spring Data Redis:

https://docs.spring.io/spring-data/redis/reference/

Spring Boot Redis:

https://docs.spring.io/spring-boot/reference/data/nosql.html

版本提醒:

Redis、Spring Boot、Spring Data Redis 都在持续更新。

学习核心原理时优先掌握:
数据结构
TTL
缓存
一致性
分布式锁
高可用

真正创建项目时,
具体配置项、序列化 API 和客户端版本
以当前官方文档为准。