Files
AIclinicalresearch/docs/07-运维文档/05-Redis缓存与队列的区别说明.md
HaHafeng dac3cecf78 feat(iit): Complete IIT Manager Agent Day 1 - Environment initialization and WeChat integration
Summary:
- Complete IIT Manager Agent MVP Day 1 (12.5% progress)
- Database: Create iit_schema with 5 tables (IitProject, IitPendingAction, IitTaskRun, IitUserMapping, IitAuditLog)
- Backend: Add module structure (577 lines) and types (223 lines)
- WeChat: Configure Enterprise WeChat app (CorpID, AgentID, Secret)
- WeChat: Obtain web authorization and JS-SDK authorization
- WeChat: Configure trusted domain (iit.xunzhengyixue.com)
- Frontend: Deploy v1.2 with WeChat domain verification file
- Frontend: Fix CRLF issue in docker-entrypoint.sh (CRLF -> LF)
- Testing: 11/11 database CRUD tests passed
- Testing: Access Token retrieval test passed
- Docs: Create module status and development guide
- Docs: Update MVP task list with Day 1 completion
- Docs: Rename deployment doc to SAE real-time status record
- Deployment: Update frontend internal IP to 172.17.173.80

Technical Details:
- Prisma: Multi-schema support (iit_schema)
- pg-boss: Job queue integration prepared
- Taro 4.x: Framework selected for WeChat Mini Program
- Shadow State: Architecture foundation laid
- Docker: Fix entrypoint script line endings for Linux container

Status: Day 1/14 complete, ready for Day 2 REDCap integration
2026-01-01 14:32:58 +08:00

16 KiB
Raw Blame History

Redis缓存 vs Redis队列 - 详细说明

文档版本: V1.0
创建日期: 2025-12-12
目标读者: 产品经理、开发人员
目的: 澄清Redis缓存和Redis队列的区别


📋 目录

  1. 核心概念
  2. 当前系统真实状态
  3. Redis缓存 vs Redis队列对比
  4. 只用Redis缓存的影响
  5. 启用Redis队列的收益与风险
  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已安装但未使用

// backend/package.json (第36行)
"bullmq": "^5.65.0",  // ← 已安装但代码中未使用

发现2当前使用自研的MemoryQueue

// 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次

// 主要使用位置:
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缓存的用途

// 场景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队列的用途

// 场景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 技术实现对比

// ==================== 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

# 参考文档04-Redis改造实施计划.md
# Phase 1-3

✅ 安装ioredis
✅ 实现RedisCacheAdapter
✅ 添加降级策略
✅ 本地测试

验收标准

✅ HealthCheckService缓存命中
✅ LLM12FieldsService缓存命中
✅ 实例重启缓存不丢失
✅ Redis挂了系统仍可用降级

阶段2Redis队列下周

Day 1实现RedisQueue

// 创建backend/src/common/jobs/RedisQueue.ts
import { Queue, Worker } from 'bullmq';

export class RedisQueue implements JobQueue {
  // ... 实现
}

Day 2迁移业务代码

// 修改screeningService.ts
// 从:
processLiteraturesInBackground(task.id, ...);

// 改为:
await jobQueue.push('asl:screening', {
  taskId: task.id,
  projectId,
  literatures
});

Day 3测试与上线

✅ 单元测试
✅ 集成测试
✅ 压力测试
✅ 故障模拟测试
✅ 灰度发布

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:可以!修改环境变量即可。

# 切换到Redis队列
QUEUE_TYPE=redis

# 回退到内存队列
QUEUE_TYPE=memory

但注意:Redis中的未完成任务会丢失

Q5BullMQ难学吗

A5不难核心API只有5个。

// 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改造实施计划