---
source: AI回答.md
updated: 2026-05-09
---

# 知习 Redis 设计

> Redis 在知习里不是"另一个 MySQL"，它是系统的**加速器和调度器**。MySQL 存结果，Redis 管过程。

---

## 1. Redis 定位

Redis 不作为主数据库，只负责：

1. 缓存
2. 限流
3. 队列（BullMQ）
4. 临时任务状态
5. 分布式锁
6. 防重复提交
7. AI 调用次数统计
8. 短期 Token / 黑名单
9. 通知任务调度
10. 学习会话草稿

---

## 2. Key 命名规范

统一格式：

```text
业务域:对象类型:对象ID:字段
```

示例：

```text
cache:user:123:profile
rate:user:123:ai:daily:2026-05-09
lock:ai-analysis:session:987
job:ai-analysis:abc123:status
```

规则：

1. 全部小写
2. 用冒号 `:` 分隔
3. 从大范围到小范围
4. userId、jobId、sessionId 明确写在 key 里
5. 带日期的 key 用 `YYYY-MM-DD`
6. 所有临时 key 必须设置 TTL

---

## 3. Key 总表

### 缓存类

| Key | 用途 | TTL |
|-----|------|-----|
| `cache:user:{userId}:profile` | 用户资料 | 5-10 分钟 |
| `cache:user:{userId}:preferences` | 用户偏好设置 | 10 分钟 |
| `cache:user:{userId}:knowledge-bases` | 用户知识库列表 | 3-5 分钟 |
| `cache:knowledge-base:{kbId}:summary` | 知识库摘要 | 5 分钟 |
| `cache:review:user:{userId}:due-count` | 到期复习数量 | 1-3 分钟 |

### 限流类

| Key | 用途 | TTL |
|-----|------|-----|
| `rate:user:{userId}:ai:daily:{date}` | 用户每日 AI 调用次数 | 到当天结束或 24h |
| `rate:user:{userId}:feedback:hourly` | 用户每小时反馈次数 | 1 小时 |
| `rate:ip:{ip}:request:{minute}` | IP 每分钟请求频率 | 60-120 秒 |
| `rate:ip:{ip}:login:{date}` | IP 每日登录尝试 | 10-30 分钟 |

### 分布式锁类

| Key | 用途 | TTL |
|-----|------|-----|
| `lock:ai-analysis:session:{sessionId}` | 防止重复提交 AI 分析 | 60-300 秒 |
| `lock:ai-analysis:answer:{answerId}` | 防止同回答重复分析 | 60-300 秒 |
| `lock:document-import:{importId}` | 防止重复处理导入 | 5-30 分钟 |
| `lock:review-plan:user:{userId}:item:{itemId}` | 防止重复生成复习计划 | 60-300 秒 |
| `lock:feedback:ip:{ip}` | 防止 IP 刷反馈 | 60-300 秒 |

### 任务状态类

| Key | Value 示例 | TTL |
|-----|-----------|-----|
| `job:ai-analysis:{jobId}:status` | `pending / processing / completed / failed` | 24h |
| `job:ai-analysis:{jobId}:progress` | `0-100` | 24h |
| `job:ai-analysis:{jobId}:error` | 错误信息字符串 | 24h |
| `job:document-import:{importId}:status` | `pending / parsing / chunking / generating / completed / failed` | 24h |
| `job:document-import:{importId}:progress` | `0-100` | 24h |
| `job:document-import:{importId}:message` | `"正在提取关键知识点"` | 24h |
| `job:document-import:{importId}:error` | 错误信息字符串 | 24h |

### 会话临时状态类

| Key | 用途 | TTL |
|-----|------|-----|
| `session:learning:{sessionId}:heartbeat` | 学习会话心跳 | 30 分钟 |
| `session:learning:{sessionId}:current-step` | 当前学习步骤 | 2 小时 |
| `session:active-recall:{sessionId}:draft` | 回答草稿暂存 | 1-24 小时 |

### Token / 黑名单

| Key | 用途 | TTL |
|-----|------|-----|
| `auth:refresh-token:blacklist:{tokenId}` | 注销后刷新 Token 失效 | 到 token 过期 |
| `auth:access-token:blacklist:{jwtId}` | 注销后 JWT 失效 | 到 token 过期 |

### Set 类（可选）

| Key | 用途 |
|-----|------|
| `set:user:{userId}:reviewed-items:{date}` | 当天已复习项去重 |

---

## 4. Redis 数据类型选择

| 类型 | 用途 | 示例 |
|------|------|------|
| **String** | 最常用：缓存 JSON、计数器、状态、锁 | `rate:user:123:ai:daily:2026-05-09 = 8` |
| **Hash** | 可选，任务多字段频繁更新的场景 | `job:ai-analysis:1001 → status=processing, progress=40` |
| **List/Stream** | 队列，BullMQ 自动管理，不需要手动操作 | - |
| **Set** | 去重，如当天已复习项集合 | `set:user:123:reviewed-items:2026-05-09` |
| **Sorted Set** | 后期按时间排序的复习调度，v0.1 先不做 | - |

---

## 5. 核心流程中 Redis 与 MySQL 的配合

### 5.1 AI 分析流程

```text
 1. MySQL 创建 ai_analysis_jobs
 2. Redis 加入 ai-analysis 队列（BullMQ）
 3. Redis 存 job:xxx:status = processing
 4. Worker 调用 AI
 5. MySQL 写 ai_analysis_results
 6. MySQL 写 focus_items
 7. MySQL 写 review_cards
 8. Redis 存 job:xxx:status = completed
 9. MySQL 写 notifications
```

### 5.2 资料导入流程

```text
 1. MySQL 创建 document_imports
 2. Redis 加入 document-import 队列（BullMQ）
 3. Redis 存导入进度
 4. Worker 解析文件
 5. MySQL 写 knowledge_items
 6. MySQL 更新 document_imports 为 success
 7. Redis 存状态 completed
```

### 5.3 学习活跃图流程

```text
 1. 用户完成学习动作
 2. MySQL 写 learning_records
 3. MySQL 更新 daily_learning_activities
 4. Redis 可短期缓存今日活跃统计
 5. App 查询活跃图优先查 MySQL，必要时加缓存
```

---

## 6. BullMQ 队列

BullMQ 自动管理 Redis key，不需要手动建。建议预留 3 个队列：

| 队列名 | 任务数据 | 处理逻辑 |
|--------|---------|---------|
| `ai-analysis` | `{ jobId, userId, sessionId, answerId, jobType }` | 读取回答 → 调 AI → 写结果 → 生成待巩固项 → 生成复习卡片 → 发通知 |
| `document-import` | `{ importId, userId, knowledgeBaseId, sourceType }` | 解析文件 → 提取文本 → 分段 → 生成知识点 → 写 knowledge_items → 更新状态 |
| `notification` | `{ userId, type, title, data }` | 写 notifications 表，后续可扩展 APNs 推送 |

---

## 7. 哪些绝对不能只放 Redis

以下全部必须写 MySQL，Redis 只能做缓存，不是唯一来源：

```text
用户资料           → users, user_profiles
知识库内容         → knowledge_bases
知识点内容         → knowledge_items
学习记录           → learning_records
主动回忆回答       → active_recall_answers
AI 分析结果        → ai_analysis_results
待巩固项           → focus_items
复习卡片           → review_cards
复习记录           → review_logs
学习活跃记录       → daily_learning_activities
通知记录           → notifications
用户设置           → user_preferences
协议同意记录       → user_consents
```

---

## 8. v0.1 Redis 最小落地范围

### 必须做

```text
1. Redis 连接
2. /health 检查 Redis
3. RedisModule + RedisService（get/set/del/exists/expire/ttl/incr/setNx/lock/unlock）
4. AI 每日调用限流（rate:user:{userId}:ai:daily:{date}）
5. AI 分析队列（BullMQ ai-analysis）
6. AI 分析任务状态（job:ai-analysis:{jobId}:status/progress/error）
7. 防重复提交锁（lock:ai-analysis:session:{sessionId}）
8. document-import 队列预留（BullMQ document-import）
9. notification 队列预留（BullMQ notification）
```

### 暂时不做

```text
复杂缓存策略
Sorted Set 复习调度
复杂分布式任务调度
全量通知推送（APNs）
复杂排行榜
```

---

## 9. RedisService 方法清单

```text
get(key)           — 读取
set(key, value)    — 写入
del(key)           — 删除
exists(key)        — 判断存在
expire(key, ttl)   — 设置过期
ttl(key)           — 查看剩余时间
incr(key)          — 自增（限流计数）
setNx(key, value)  — 不存在才写入（锁）
lock(key, ttl)     — 获取分布式锁，返回 token
unlock(key, token) — 释放锁，校验 token，防止误删
```

锁的实现注意：

- 锁必须设置 TTL
- 解锁时必须校验 value/token，不能误删别人的锁
- 锁的 value 用随机 token，解锁时比对

---

## 10. 一句话总结

> **Redis 在知习里不是"另一个 MySQL"，它是系统的加速器和调度器。MySQL 存结果，Redis 管过程。**
