CodeRoadMap
首页路线题库资源库文章社区
CodeRoadMap

程序员的学习成长路线图。内容免费开放,登录后可同步路线图与课时进度。

由 程序员柏风 独立维护 · 关于作者

学习

  • 路线图
  • 课程
  • 面试题
  • 文章
  • 我

更多

  • 资源
  • 社区
  • 关于作者
  • 内容说明

© 2026 CodeRoadMap

津ICP备2026012044号-1·津公网安备12011002026155号·coderoadmap@126.com
Redis 缓存入门/课时/会话与 Sa-Token
RawBlame
课时目录12 节

Redis 缓存入门

  1. 01Redis 是什么
  2. 02CLI 与键命名
  3. 03String 与计数
  4. 04Hash / List / Set / ZSet
  5. 05过期与淘汰
  6. 06缓存模式
  7. 07Spring Data Redis
  8. 08会话与 Sa-Token
  9. 09Docker 运行 Redis
  10. 10Pub/Sub 入门
  11. 11测试与监控
  12. 12综合练习

会话与 Sa-Token

本站 sa-token-redis-jackson、分布式 session。

30 分钟

分布式会话:登录态为什么必须进 Redis

这一课你将能做到

  1. 画出「内存会话 + 多实例」的失败现场,说清为什么登录态必须集中存储
  2. 真实登录一次,用 redis-cli 找到 satoken: 前缀的会话 key(只读,不删),说出 Sa-Token 在 Redis 里存了什么、TTL 是多少
  3. 解释 timeout=86400、is-concurrent、is-share 三个配置的含义和产品效果,并演示开关关闭时会话落回内存

为什么要学

本站登录用的是 Sa-Token,默认把会话存在 JVM 内存里。开发时单实例没问题;但生产是多实例(或将来扩容、重启):

  • 重启即全员下线:进程一死,内存里的登录态全没,所有用户重新登录
  • 多实例登录精神分裂:Nginx 把请求轮询到 1 号实例登录,下一个请求落到 2 号实例——2 号说「你谁啊」,要求重新登录;每次请求都可能在实例间弹跳

解法只有一个:会话放到所有实例都能读的中心存储——Redis。本站生产 profile 里 codeforge.redis.enabled=true 时,sa-token-redis-jackson 自动接管会话存储。这一课让你亲眼看到它写了什么。

先建立图像

【内存会话 + 2 实例】
浏览器 ──token──▶ Nginx ──▶ 实例1(内存里有 token ✅)
                 │
                 └──▶ 实例2(内存里没有 ❌ → 401,请重新登录)
                (轮询命中不同实例 → 登录态时有时无)

【Redis 会话 + 2 实例】
浏览器 ──token──▶ Nginx ──▶ 实例1 ─┐
                 │                 ├─ 都查同一个 Redis → 结果一致 ✅
                 └──▶ 实例2 ───────┘

跟着做

步骤 1:读本站的 Sa-Token 配置

backend/src/main/resources/application.yml:

sa-token:
  token-name: Authorization   # 前端请求头名字
  timeout: 86400              # 会话有效期:24 小时(秒)
  is-concurrent: true         # 同一账号允许多端同时登录
  is-share: true              # 多端登录共享同一个 token
  token-style: uuid           # token 样式:随机 UUID

这些配置的每个值都会变成 Redis 里的一条记录或一个 TTL——下一步亲眼验证。

步骤 2:打开开关并启动 backend

本课需要真实登录,把本机开关临时打开(Redis 已在跑):

codeforge:
  redis:
    enabled: true   # 临时改,本课结束改回

启动 backend(IDE 或 mvnw spring-boot:run),日志确认 RedisConfig 的 Bean 生效。

步骤 3:真实登录一次

用测试账号登录(没有就先走注册接口造一个):

curl -s -X POST localhost:8080/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{"username":"你的账号","password":"你的密码"}'
# 返回体里有 tokenValue(token-style: uuid 的样子)

步骤 4:去 Redis 里找会话 key

docker exec -it redis-dev redis-cli
SCAN 0 MATCH satoken:* COUNT 100

你会看到类似:

satoken:login:token:<uuid>        ← token → loginId 的映射
satoken:login:session:<loginId>   ← 账号级会话对象

用 TYPE 和 TTL 看它们:

TYPE satoken:login:token:<uuid>    # string(值是 loginId 相关 JSON)
TTL satoken:login:token:<uuid>     # ≈86400,随活跃滚动续期

纪律:只看,不 DEL、不改名——删了会话等于把真实用户踢下线。本机踢的是你自己没损失,但习惯要现在养成。

步骤 5:验证 token 校验走的是 Redis

拿 token 调一个需要登录的接口:

curl -s localhost:8080/api/user/me -H "Authorization: <token>"

Sa-Token 校验 token 的过程:拿请求头里的 token 拼 key 查 Redis → 命中则取 loginId 放进请求上下文。想亲眼验证,可以在本机临时开 MONITOR 观察这次请求打出的 GET satoken:login:token:... 命令,看完立刻 Ctrl+C(MONITOR 生产禁用,下一课细讲为什么)。

步骤 6:把开关关回去,体会降级

codeforge:
  redis:
    enabled: false   # 改回

重启后重新登录,再 SCAN satoken:*——Redis 里没有新 key 了:会话落回了 Sa-Token 的内存实现。同一套业务代码,存储位置由开关决定,这就是第 7 课说的「整组换轨」。

讲清楚要点

  1. 会话 key 的 TTL 就是登录有效期:timeout=86400 → 写入时 EXPIRE 24 小时;活跃访问滚动续期,久不用自然过期下线——Redis 的 TTL 机制和产品语义完美对应。
  2. is-concurrent + is-share 决定 token 与账号的映射形状:is-share=true 时多端共用同一个 satoken:login:token: 条目;关掉则每端一个 token、各自 TTL。
  3. sa-token-redis-jackson 的原理 = 实现 SaTokenDao 接口:所有 get/set/delete 会话的调用被路由到 RedisTemplate;本站 RedisConfig 里的 SaTokenDaoForRedisTemplate + dao.init(factory) 就是接线处。
  4. token 本身是无意义的随机串:安全性来自「token 只在服务端(现在是在 Redis)有映射」,泄露了可以单点踢下线(DEL 对应 key 或走管理接口)。
  5. 内存会话不是不能用,是只能单实例:本机开发 enabled=false 反而更快更简单——架构决策跟着部署形态走。

你可能会懵的地方

  • JWT 不也能无状态多实例吗,为什么要 Redis? JWT 校验不需要中心存储,但无法主动踢人下线(签发后服务端管不了)。会话进 Redis = 可随时 DEL 强制下线、可查在线人数。本站选 Sa-Token + Redis 就是看重可控性。

  • 会话丢了用户会怎样? 下次请求校验失败 → 前端引导重新登录。体验损失可接受,数据无损——这正是第 1 课「Redis 数据丢失后果分级」的实例:缓存丢了重建,会话丢了重登,谁都不能丢 MySQL。

  • Redis 里的会话被 LRU 挤掉怎么办? 用户被登出。所以会话实例要容量规划、监控 maxmemory(第 11 课 INFO),或会话/缓存分实例部署。

  • 为什么 token 请求头叫 Authorization? sa-token.token-name 配置的,本站与常见 HTTP 规范对齐;要改只动配置。

  • 多实例下 Caffeine 那 30 秒会影响会话吗? 不会。Caffeine 只缓存业务只读数据(tracks 等),会话每次都查 Redis,保证踢人即时生效。

错法对照

错法问题正解
多实例 + 内存会话登录态随实例漂移、重启全员掉线Sa-Token + sa-token-redis-jackson
测试时随手 DEL satoken:*把自己/他人踢下线只读验证:TYPE/TTL/SCAN
timeout 设超大(如一年)会话堆积占内存合理有效期 + 滚动续期
会话和缓存挤同一个小内存实例LRU 挤掉会话 → 用户被踢分实例或容量规划 + 监控
token 明文写进日志泄露风险脱敏;本站生产 cookie.secure=true

卡点急救

  1. 登录成功但接口 401:请求头名字要对(Authorization),值就是 token 本体;本机确认开关 true 且 Redis PING 通。
  2. SCAN 不到 satoken key:开关没开(会话在内存里);或登录用的是改开关之前的旧 token。
  3. 重启 backend 后掉线:开关 false → 会话在内存。打开开关再登录。
  4. token TTL 不刷新:确认没被手工 PERSIST;Sa-Token 活跃续期依赖 auto-renew(默认开)。
  5. 想踢某人下线:走管理端接口或 Sa-Token 的 API;应急可 DEL satoken:login:token:<token>(生产别裸删,容易误伤)。

面试常问

  • 多实例下登录态怎么共享? 中心化会话存储:Sa-Token 实现 SaTokenDao 接到 RedisTemplate,登录写 satoken:login:token:{uuid},校验时各实例查同一个 Redis,TTL=登录有效期、活跃滚动续期。

  • Session、Redis 会话、JWT 的取舍? 内存 Session:最快但限单机;Redis 会话:可控踢人、多实例共享,代价一次网络查询;JWT:无状态但难撤销。内容站选 Redis 会话,平衡可控与简单。

  • 怎么实现「强制下线/踢人」? 删掉该用户在 Redis 里的 token 映射(或 Sa-Token API),下一次请求校验失败即下线——这是无状态 JWT 做不到的。

自测三问

  1. 内存会话在多实例下的两个具体故障是什么?
  2. satoken:login:token: 和 satoken:login:session: 分别存什么?各自的 TTL 由什么配置决定?
  3. timeout=86400 在 Redis 层面靠什么机制实现?「滚动续期」靠什么触发?

小结一句话

会话进 Redis = 多实例共享、重启不丢、可踢可查;本站靠 sa-token-redis-jackson 一个 Bean 完成接线,开关一关还能整体落回内存。

延伸

  • Sa-Token 集成 Redis
  • 下一课:Docker 与持久化——生产怎么跑 Redis、RDB/AOF 怎么选、为什么没有卷的数据一删容器就蒸发。
加载本节练习…
← 上一课Spring Data Redis下一课 →Docker 运行 Redis