核心观点:Redis 解决的不是“CPU 忙不忙”,而是“HTTP 连接能不能等”。

我们把你的 RAG 系统拆解来看:

场景一:CPU 密集型任务(PDF 解析、OCR)

  • 现象:服务器 CPU 飙升到 100%,风扇狂转。

  • 如果不加 Redis

    • Python 的 GIL(全局解释器锁)会导致主线程卡死。
    • API 服务器完全失去响应,连简单的“健康检查”接口都回不了。
    • 后果:系统崩溃(Crash / Unresponsive)。
  • Redis 的作用“隔离”。把重活扔给 Worker 进程,保住 API 服务器的命。

场景二:I/O 密集型任务(DeepSeek / OpenAI 生成方案)

  • 现象:CPU 占用率极低(可能只有 1%),因为服务器只是在傻傻地等待 DeepSeek 返回数据。
  • 如果不加 Redis
    • HTTP 超时(Timeouts):这是最致命的。浏览器、Nginx 网关、负载均衡器通常都有 60秒 的默认超时时间。如果 DeepSeek 生成一个长方案需要 90秒,前端连接会被直接切断,用户看到“504 Gateway Timeout”,虽然你的后端还在跑,但结果发不回去了。
    • Worker 耗尽(Worker Starvation):假设你用 uvicorn 启动了 4 个 Worker。如果来了 4 个用户做长文本分析,这 4 个 Worker 虽然 CPU 不累,但都处于“占线”状态(Pending)。第 5 个用户想登录,发现没人理他
    • 任务中断风险:用户觉得慢,手滑关掉了浏览器页面。如果是纯 API 连接,连接断开可能导致后端任务取消(浪费了 API 调用的钱)。
  • Redis 的作用“解耦”
    • API 服务器收到请求,0.1秒返回“任务ID”,连接结束。
    • Worker 在后台慢慢等 DeepSeek,等 5 分钟都没关系,反正不需要维持 HTTP 连接。
    • 用户关掉网页,Worker 依然在跑,结果存入数据库,用户下次回来还能看到。

场景三:流量整形(Traffic Shaping / Rate Limiting)

  • 现象:你的 API Key 每分钟只能调用 60 次。突然来了 1000 个用户请求。
  • 如果不加 Redis
    • 即使你用了 asyncio.Semaphore,这 1000 个 HTTP 请求也会堆积在你的服务器内存里,消耗大量文件句柄(File Descriptors)和内存。
    • 一旦服务器重启,这 1000 个请求全部丢失。
  • Redis 的作用“削峰”
    • 1000 个任务瞬间进入 Redis 队列(Redis 每秒能处理 10万+ 写入,毫无压力)。
    • 后台 Worker 只有 5 个,它们会慢条斯理地每次取 5 个任务做。
    • 结果:用户感觉“排队中”,但系统稳如泰山,绝对不会触发 API 限流报错。
      本地跑通Redis

但是我在论文向量化搜索以及LLM精读的时候,就不需要使用Redis,主要在于区分在线任务和离线任务

1. 为什么搜索不适合用 Redis?

(1) 用户体验:即时性 vs. 轮询

  • 搜索场景(Search/Rerank)
    • 用户在搜索框输入关键词,按回车。
    • 心理预期:“马上给我结果”(1-3秒,最多5秒)。
    • Asyncio 模式:请求发出去 -> 所有的等待都在 Socket 层 -> 结果直接返回。耗时 = LLM 响应时间
    • Redis 模式:请求 -> 入队 -> Worker 抢单 -> 执行 -> 写入 Redis -> 前端轮询(Polling)
      • 前端每隔 1 秒查一次:“好了吗?”
      • 这会引入不必要的 轮询延迟。原本 2.1 秒能做完的事,可能因为轮询间隔变成 3.0 秒。

(2) 数据传输开销 (Serialization Overhead)

  • 流程对比
    • Asyncio:向量搜出的 20 篇论文对象(Dict)直接在内存里传递给 score_papers_listwise零拷贝,零开销
    • Redis:你需要把这 20 篇论文(包含摘要、标题等)序列化成 JSON/Pickle -> 通过网络发给 Redis -> Worker 从 Redis 拉取并反序列化。
    • 后果:这增加了不必要的 CPU 开销和 Redis 带宽压力。

(3) 复杂性爆炸

  • 如果你把搜索也做成异步 Task,前端逻辑会变得极度复杂:
    • 用户点搜索 -> 拿到 Task ID -> 显示加载条 -> 轮询状态 -> 拿到结果列表。
    • 对于一个“搜索框”来说,这太重了。

2. 什么时候搜索才需要 Redis?

只有当你的搜索不是普通的 RAG,而是 Deep Research(深度研究) 模式时,才需要 Redis。
对比一下:

特征 普通搜索 / Rerank (你现在的) 深度研究 (Deep Research)
动作 搜向量库 + LLM 给前10名打个分 联网搜 Google + 爬取 50 个网页 + 阅读 + 写报告
耗时 1 ~ 5 秒 30 秒 ~ 5 分钟
瓶颈 LLM API 响应速度 网络带宽 + 网页解析 + 多轮 LLM 推理
架构 Asyncio (FastAPI 原地处理) Redis 任务队列
UX 菊花转圈,直接出结果 进度条,分步显示

在本地跑通

1
2
3
4
 ~ ▓▒░ sudo service redis-server start             ░▒▓ ✔ │ root@gyx │ 19:35:37 
 ~ ▓▒░ sudo ss -ltnp | grep redis ░▒▓ ✔ │ root@gyx │ 19:35:39 
LISTEN 0 511 0.0.0.0:6379 0.0.0.0:* users:(("redis-server",pid=306,fd=6))

1
2
3
4
5
6
7
8
 server  Test-NetConnection -ComputerName localhost -Port 6379
WARNING: TCP connect to (::1 : 6379) failed
ComputerName : localhost
RemoteAddress : 127.0.0.1
RemotePort : 6379
InterfaceAlias : Loopback Pseudo-Interface 1
SourceAddress : 127.0.0.1
TcpTestSucceeded : True

说明Windows 可以通过 IPv4 (127.0.0.1) 连通 Redis,但无法通过 IPv6 (::1) 连通。

1
2
3
4
5
6
7
8
9
10
11
12
13
# 探测本地 Redis 是否可用
try:
        test_client = aioredis.Redis(host="localhost", port=6379, socket_timeout=1)
        await test_client.ping()
        await test_client.close()
        # 建立 ARQ 连接池
        app.state.redis_pool = await create_pool(
            RedisSettings(host="localhost", port=6379)
        )
        app.state.use_redis = True
        print("✅ 检测到 Redis,已开启基于 ARQ 的实现路径任务队列模式")
    except Exception as e:
        print(f"⚠️ Redis 未检测到或连接失败({e}),回退为本地实现路径任务模式")

Python 的 aioredis 库解析 "localhost" 时,往往会**优先尝试 IPv6 (::1)**。
所以把所有localhost改成127.0.0.1