Files
AIclinicalresearch/docs/07-运维文档/05-Redis缓存与队列的区别说明.md
HaHafeng 2481b786d8 deploy: Complete 0126-27 deployment - database upgrade, services update, code recovery
Major Changes:
- Database: Install pg_bigm/pgvector plugins, create test database
- Python service: v1.0 -> v1.1, add pymupdf4llm/openpyxl/pypandoc
- Node.js backend: v1.3 -> v1.7, fix pino-pretty and ES Module imports
- Frontend: v1.2 -> v1.3, skip TypeScript check for deployment
- Code recovery: Restore empty files from local backup

Technical Fixes:
- Fix pino-pretty error in production (conditional loading)
- Fix ES Module import paths (add .js extensions)
- Fix OSSAdapter TypeScript errors
- Update Prisma Schema (63 models, 16 schemas)
- Update environment variables (DATABASE_URL, EXTRACTION_SERVICE_URL, OSS)
- Remove deprecated variables (REDIS_URL, DIFY_API_URL, DIFY_API_KEY)

Documentation:
- Create 0126 deployment folder with 8 documents
- Update database development standards v2.0
- Update SAE deployment status records

Deployment Status:
- PostgreSQL: ai_clinical_research_test with plugins
- Python: v1.1 @ 172.17.173.84:8000
- Backend: v1.7 @ 172.17.173.89:3001
- Frontend: v1.3 @ 172.17.173.90:80

Tested: All services running successfully on SAE
2026-01-27 08:13:27 +08:00

763 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Redis缓存 vs Redis队列 - 详细说明
> **文档版本:** V1.0
> **创建日期:** 2025-12-12
> **目标读者:** 产品经理、开发人员
> **目的:** 澄清Redis缓存和Redis队列的区别
---
## 📋 目录
1. [核心概念](#1-核心概念)
2. [当前系统真实状态](#2-当前系统真实状态)
3. [Redis缓存 vs Redis队列对比](#3-redis缓存-vs-redis队列对比)
4. [只用Redis缓存的影响](#4-只用redis缓存的影响)
5. [启用Redis队列的收益与风险](#5-启用redis队列的收益与风险)
6. [推荐方案](#6-推荐方案)
---
## 1. 核心概念
### 1.1 什么是Redis
**Redis = 一个高性能的内存数据库**
可以理解为:
- 🏪 **超大的共享内存存储空间**
- 🚀 **读写速度极快**1ms内
- 💾 **支持数据持久化**
- 🌐 **多个服务器可以共享**
Redis本身是一个**工具箱**,里面有很多工具(数据结构):
- String字符串—— 用于缓存
- List列表—— 用于队列
- Set集合
- Hash哈希表
- Sorted Set有序集合
### 1.2 Redis缓存 vs Redis队列
这两个概念是**使用Redis的不同方式**,不是两个不同的产品!
```
阿里云Redis实例您购买的
可以同时用于:
├─ Redis缓存用String数据结构
└─ Redis队列用List数据结构 + BullMQ库
```
**类比理解**
```
Redis = 一栋大楼(您购买的)
Redis缓存 = 大楼里的"快递柜"
- 存东西、取东西
- 有过期时间
- 用途:快速查询
Redis队列 = 大楼里的"传送带"
- 任务排队
- 按顺序处理
- 用途:异步任务
```
---
## 2. 当前系统真实状态
### 2.1 代码检查结果 ✅
#### **发现1BullMQ已安装但未使用**
```json
// backend/package.json (第36行)
"bullmq": "^5.65.0", // ← 已安装但代码中未使用
```
#### **发现2当前使用自研的MemoryQueue**
```typescript
// backend/src/common/jobs/JobFactory.ts
export class JobFactory {
private static createQueue(): JobQueue {
const queueType = process.env.QUEUE_TYPE || 'memory'; // ← 默认memory
switch (queueType) {
case 'memory':
return this.createMemoryQueue(); // ← 当前使用这个
case 'database':
// TODO: 实现DatabaseQueue // ← 还没实现
return this.createMemoryQueue();
}
}
}
```
#### **发现3jobQueue在8个文件中被引用共50次**
```typescript
// 主要使用位置:
1. ExtractionController.ts (TODO注释)
2. DualModelExtractionService.ts (TODO注释)
3. test-platform-api.ts ()
4. test-platform-infrastructure.ts ()
```
### 2.2 真实情况总结
| 组件 | 安装状态 | 使用状态 | 说明 |
|------|---------|---------|------|
| **Redis缓存** | ❌ 未配置 | ❌ 未使用 | 目前用的是MemoryCache |
| **Redis队列** | ❌ 未配置 | ❌ 未使用 | BullMQ已安装但未启用 |
| **内存缓存** | ✅ 已实现 | ✅ 使用中 | HealthCheck、LLM结果 |
| **内存队列** | ✅ 已实现 | ✅ 使用中 | MemoryQueue |
**结论**
- ✅ 您的系统已经预留了队列架构MemoryQueue
- ✅ 已经安装了BullMQ依赖
- ❌ 但实际还没有真正使用Redis无论是缓存还是队列
---
## 3. Redis缓存 vs Redis队列对比
### 3.1 功能对比
| 维度 | Redis缓存 | Redis队列 |
|------|----------|----------|
| **用途** | 存储临时数据 | 异步任务管理 |
| **数据结构** | String字符串 | List列表 |
| **操作** | GET / SET / DELETE | PUSH / POP / ACK |
| **过期机制** | TTL自动过期 | 任务完成后删除 |
| **读写模式** | 随机读写 | 顺序处理FIFO |
| **典型场景** | LLM结果缓存、Session | 文献筛选任务、批量处理 |
| **库/工具** | 直接用ioredis | BullMQ基于ioredis |
### 3.2 在您系统中的实际应用
#### **Redis缓存的用途**
```typescript
// 场景1LLM结果缓存
const cacheKey = `fulltext:${literatureId}:${model}`;
await cache.set(cacheKey, llmResult, 3600); // 缓存1小时
// 下次相同PDF再提取时
const cached = await cache.get(cacheKey);
if (cached) {
return cached; // ✅ 直接返回节省API费用
}
```
**好处**
- ✅ 避免重复调用DeepSeek API¥0.43/篇)
- ✅ 响应速度快从1分钟降到1ms
- ✅ 多实例共享缓存
---
#### **Redis队列的用途**
```typescript
// 场景2文献筛选任务199篇需要30-60分钟
export async function startScreening(projectId) {
// 1. 创建任务(不阻塞请求)
const task = await prisma.aslScreeningTask.create({...});
// 2. 推送到队列(异步处理)
await jobQueue.push('asl:screening', {
taskId: task.id,
projectId,
literatureIds: [1, 2, 3, ..., 199]
});
// 3. 立即返回(前端开始轮询进度)
return { taskId: task.id, status: 'pending' };
}
// 后台Worker处理队列
jobQueue.process('asl:screening', async (job) => {
for (const id of job.data.literatureIds) {
await processLiterature(id);
await jobQueue.updateProgress(job.id, ...);
}
});
```
**好处**
- ✅ 任务持久化(实例重启不丢失)
- ✅ 支持任务优先级
- ✅ 支持任务重试
- ✅ 多实例间任务分配
---
### 3.3 技术实现对比
```typescript
// ==================== Redis缓存 ====================
import Redis from 'ioredis';
const redis = new Redis({
host: 'localhost',
port: 6379
});
// 写入缓存
await redis.set('key', 'value', 'EX', 3600);
// 读取缓存
const value = await redis.get('key');
// ==================== Redis队列 ====================
import { Queue, Worker } from 'bullmq';
// 创建队列
const queue = new Queue('asl:screening', {
connection: {
host: 'localhost',
port: 6379
}
});
// 添加任务
await queue.add('screening', { projectId: 123 });
// 创建Worker处理任务
const worker = new Worker('asl:screening', async (job) => {
// 处理任务
await processTask(job.data);
}, {
connection: { host: 'localhost', port: 6379 }
});
```
**关键点**
- ✅ 两者都连接到同一个Redis实例
- ✅ 可以同时使用(互不干扰)
- ✅ BullMQ内部也是用ioredis实现的
---
## 4. 只用Redis缓存的影响
### 4.1 可以满足的需求 ✅
1. **LLM结果缓存**
- 避免重复API调用
- 节省成本
- 提升响应速度
2. **Session管理**
- 多实例共享Session
- 用户状态同步
3. **健康检查缓存**
- 避免重复解析Excel
4. **短期任务**
- < 10秒的任务
- 直接在HTTP请求中处理
### 4.2 无法满足的需求 ❌
1. **长时间任务持久化**
```
场景用户提交199篇文献筛选需要30-60分钟
如果只用Redis缓存
- 任务状态存在内存 → 实例重启丢失
- 无法恢复中断的任务
- 用户看到任务消失
```
2. **任务队列管理** ❌
```
场景10个用户同时提交批量任务
如果只用Redis缓存
- 无法排队(先来先服务)
- 无法限制并发
- 服务器可能被打爆
```
3. **任务重试** ❌
```
场景:某篇文献处理失败
如果只用Redis缓存
- 无法自动重试
- 需要用户重新提交
```
4. **分布式任务分配** ❌
```
场景SAE有3个实例
如果只用Redis缓存
- 无法协调哪个实例处理哪个任务
- 可能重复处理
```
### 4.3 实际影响评估
#### **如果只启用Redis缓存**
| 影响维度 | 评分 | 说明 |
|---------|------|------|
| **LLM成本控制** | ✅ 优秀 | 缓存命中节省30-50%费用 |
| **多实例支持** | ✅ 优秀 | 缓存共享 |
| **长任务可靠性** | ⚠️ 一般 | 仍然依赖MemoryQueue |
| **任务管理能力** | ⚠️ 一般 | 无优先级、重试 |
| **系统可扩展性** | 🟡 中等 | 单实例可以,多实例有问题 |
---
## 5. 启用Redis队列的收益与风险
### 5.1 收益分析 💰
#### **收益1任务持久化**
```
当前MemoryQueue
- 用户提交批量任务
- 实例重启 → ❌ 任务丢失
- 用户投诉率5-10%
改用Redis队列
- 任务保存在Redis
- 实例重启 → ✅ 任务继续
- 用户投诉率:< 1%
```
#### **收益2任务重试**
```
当前MemoryQueue
- LLM调用失败 → ❌ 任务标记为失败
- 需要用户重新提交
改用Redis队列
- LLM调用失败 → ✅ 自动重试3次
- 指数退避2秒、4秒、8秒
```
#### **收益3分布式任务分配**
```
当前MemoryQueue
- 每个实例独立处理任务
- 无法协调
改用Redis队列
- 多个Worker抢占任务
- 自动负载均衡
- 某个Worker挂了其他Worker接管
```
#### **收益4任务监控**
```
当前MemoryQueue
- 任务状态在内存中
- 无法查看历史任务
改用Redis队列
- 任务历史保存
- 可以查看失败原因
- 统计处理时长
```
### 5.2 风险分析 ⚠️
#### **风险1增加复杂度** 🟡 中等
```
当前:
- MemoryQueue简单、易理解
- 代码量:~200行
改为BullMQ
- 需要学习BullMQ API
- 代码量:~500行
- 需要配置Worker
```
**缓解**
- ✅ BullMQ文档完善
- ✅ 社区活跃GitHub 15k+ stars
- ✅ 我们已经安装了依赖
---
#### **风险2依赖Redis稳定性** 🟡 中等
```
场景Redis挂了
- Redis缓存可以降级到内存
- Redis队列无法降级队列必须持久化
```
**缓解**
- ✅ 您购买的是高可用版Redis99.95%
- ✅ 主从自动切换
- ✅ 实际风险很低
---
#### **风险3调试难度增加** 🟢 低
```
当前:
- console.log() 即可
- 任务在内存中
改为BullMQ
- 需要查看Redis数据
- 需要用BullBoard可视化工具
```
**缓解**
- ✅ BullBoard提供Web UI
- ✅ 可以看到任务状态、重试次数
- ✅ 日志更详细
---
#### **风险4内存占用** 🟢 低
```
担心Redis队列会占用很多内存
实际:
- 单个任务数据:~1KB
- 100个并发任务100KB
- 1000个任务历史1MB
- 对256MB Redis影响很小
```
---
### 5.3 风险矩阵
| 风险 | 严重性 | 概率 | 缓解难度 | 建议 |
|------|--------|------|----------|------|
| **增加复杂度** | 🟡 中 | 🔴 高 | ✅ 易 | 提供培训文档 |
| **Redis依赖** | 🔴 高 | 🟢 低 | ✅ 易 | 高可用版 |
| **调试困难** | 🟢 低 | 🟡 中 | ✅ 易 | 使用BullBoard |
| **内存占用** | 🟢 低 | 🟢 低 | ✅ 易 | 监控即可 |
**结论:风险可控,收益大于风险**
---
## 6. 推荐方案
### 6.1 渐进式实施(推荐)✅
```
阶段1本周: 启用Redis缓存
├─ 目标解决LLM成本问题
├─ 工作量2天
├─ 风险:低(有降级方案)
└─ 收益节省30-50% API费用
阶段2下周: 启用Redis队列
├─ 目标:解决长任务可靠性
├─ 工作量3天
├─ 风险:中(但可控)
└─ 收益任务丢失率降低10倍
阶段3未来: 优化与监控
├─ BullBoard可视化
├─ 任务优先级
├─ 性能监控
└─ 告警机制
```
### 6.2 详细实施计划
#### **阶段1Redis缓存本周**
**Day 1-2实现RedisCacheAdapter**
```bash
# 参考文档04-Redis改造实施计划.md
# Phase 1-3
✅ 安装ioredis
✅ 实现RedisCacheAdapter
✅ 添加降级策略
✅ 本地测试
```
**验收标准**
```bash
✅ HealthCheckService缓存命中
✅ LLM12FieldsService缓存命中
✅ 实例重启缓存不丢失
✅ Redis挂了系统仍可用降级
```
---
#### **阶段2Redis队列下周**
**Day 1实现RedisQueue**
```typescript
// 创建backend/src/common/jobs/RedisQueue.ts
import { Queue, Worker } from 'bullmq';
export class RedisQueue implements JobQueue {
// ... 实现
}
```
**Day 2迁移业务代码**
```typescript
// 修改screeningService.ts
// 从:
processLiteraturesInBackground(task.id, ...);
// 改为:
await jobQueue.push('asl:screening', {
taskId: task.id,
projectId,
literatures
});
```
**Day 3测试与上线**
```bash
✅ 单元测试
✅ 集成测试
✅ 压力测试
✅ 故障模拟测试
✅ 灰度发布
```
---
### 6.3 最小改动方案(如果资源有限)
**如果只有1周时间建议**
```
优先级1必须Redis缓存
- 解决LLM成本问题
- 工作量2天
优先级2可选Redis队列
- 暂时保持MemoryQueue
- 记录技术债务
- 等用户规模增长后再改造
```
**判断标准**
```
如果满足以下条件可以暂缓Redis队列
✅ 用户数 < 20
✅ SAE只有1个实例
✅ 批量任务不频繁(每天 < 5个
✅ 可以接受偶尔任务丢失
否则建议尽快启用Redis队列
```
---
### 6.4 为什么BullMQ已经安装但未使用
**推测原因**
1. **计划使用但未实施**开发时计划用BullMQ先安装了依赖
2. **测试过但未启用**:可能在测试环境验证过
3. **依赖传递**其他包依赖了BullMQ
**建议**
- ✅ 保留BullMQ依赖已经安装了
- ✅ 在阶段2时直接使用无需重新安装
- ✅ 如果暂时不用,也不用删除(不影响性能)
---
## 7. 常见问题 FAQ
### Q1Redis缓存和Redis队列能同时使用吗
**A1**可以它们使用同一个Redis实例但是不同的数据结构。
```
Redis实例256MB
├─ 缓存数据String占用 ~50MB
└─ 队列数据List占用 ~10MB
------------------------------------
总计:~60MB / 256MB = 23% 使用率
```
### Q2不用Redis队列用数据库队列可以吗
**A2**:可以但不推荐。
| 方案 | 优势 | 劣势 |
|------|------|------|
| **Redis队列** | 快(< 1ms、功能完善 | 需要Redis |
| **数据库队列** | 不需要额外依赖 | 慢(> 10ms、功能简陋 |
您已经购买了Redis建议直接用Redis队列。
### Q3如果只启用Redis缓存系统会有问题吗
**A3**:短期内不会有大问题,但存在风险。
```
✅ 可以正常运行:
- LLM成本控制 ✅
- 多实例缓存共享 ✅
⚠️ 潜在风险:
- 长任务可能丢失(实例重启)
- 无法重试失败任务
- 用户体验不佳
```
### Q4启用Redis队列后能否回退到MemoryQueue
**A4**:可以!修改环境变量即可。
```bash
# 切换到Redis队列
QUEUE_TYPE=redis
# 回退到内存队列
QUEUE_TYPE=memory
```
但注意:**Redis中的未完成任务会丢失**。
### Q5BullMQ难学吗
**A5**不难核心API只有5个。
```typescript
// 1. 创建队列
const queue = new Queue('task');
// 2. 添加任务
await queue.add('job', { data });
// 3. 创建Worker
const worker = new Worker('task', handler);
// 4. 监听事件
worker.on('completed', ...);
worker.on('failed', ...);
// 5. 查询任务
const job = await queue.getJob(jobId);
```
预计学习时间:**半天**
---
## 8. 总结
### 8.1 核心要点
1. **Redis缓存 ≠ Redis队列**
- 都是使用同一个Redis实例
- 只是使用方式不同
2. **您的系统现状**
- BullMQ已安装但未使用
- 当前用MemoryQueue自研内存队列
- 需要改造才能启用Redis队列
3. **推荐方案**
- ✅ 先启用Redis缓存本周2天
- ✅ 再启用Redis队列下周3天
- ✅ 渐进式实施,降低风险
4. **只用Redis缓存的影响**
- ✅ 可以解决LLM成本问题
- ⚠️ 长任务可靠性仍有风险
- 📊 建议:根据用户规模决定是否启用队列
5. **Redis队列的风险**
- 🟡 增加复杂度(但可控)
- 🟢 依赖Redis稳定性高可用版99.95%
- ✅ 收益 > 风险
---
### 8.2 决策建议
**如果您的系统满足以下条件之一建议尽快启用Redis队列**
```
✅ 用户数 > 20
✅ SAE实例数 > 1
✅ 批量任务频繁(每天 > 5个
✅ 任务时长 > 10分钟
✅ 用户抱怨任务丢失
```
**否则可以先启用Redis缓存队列暂缓。**
---
**文档维护者:** 技术团队
**最后更新:** 2025-12-12
**相关文档:** [Redis改造实施计划](./04-Redis改造实施计划.md)