RAG 高并发架构设计之四道防护体系
一、四道防护体系全景
在建立了四段式管线的性能模型之后,接下来进入生产级解决方案:四道核心防护体系。这套体系贯穿 RAG 全链路,从事前观测、事中调度到事后兜底三个维度,系统性解决队列积压、长尾延迟、算力空耗、数据失真四大问题。
1.1 防护一:全链路分段可观测 — 精准定位瓶颈
1.1.1 传统全局监控的致命缺陷
多数中小团队的 RAG 服务监控仅停留在粗粒度全局指标层面——统计整个服务的总 QPS、全链路平均延迟、整机 CPU/GPU 使用率。
问题在于,RAG 是四段串行流水线架构,各阶段的硬件依赖和故障特征完全不同:
- Embedding 为 CPU 密集型,瓶颈在 CPU 利用率
- 向量检索为内存 + IO 密集型,瓶颈在内存带宽和磁盘 IOPS
- Rerank 为轻量 GPU 推理,瓶颈在 GPU 计算队列
- LLM 为重型 GPU 推理,瓶颈在 KV Cache 显存和推理队列长度
全局平均延迟掩盖了单段瓶颈。就像一个人体温"平均 37°C",但其实是头 40°C、脚 34°C——局部已经着火了。
1.1.2 大厂生产标准:四段独立可观测体系
生产级 RAG 服务的监控体系要求对四个链路独立埋点、独立采集、独立告警、独立大盘,实现故障秒级定位。
各分段核心观测项与运维标准:
| 阶段 | 资源指标 | 性能指标 | 质量指标 | 告警阈值(建议) |
|---|---|---|---|---|
| Embedding | CPU 利用率 | 编码耗时 P99、单节点 QPS | 缓存命中率 | CPU > 80%、P99 > 50ms |
| 向量检索 | 内存占用、磁盘 IOPS | 查询耗时 P99、DB QPS | ANN 召回率、索引命中率 | P99 > 100ms、召回率 < 90% |
| Rerank | GPU 利用率、显存占用 | 推理耗时 P99 | 重排通过率(分数 > 0.3 比例) | GPU > 90%、P99 > 300ms |
| LLM 生成 🔴 | KV Cache 占用率、GPU 利用率 | 推理队列长度、Token 生成速度、P50/P99/P999 延迟 | 超时率、首 Token 延迟 | 队列长度 > 100、P99 > 5s、KV Cache > 85% |
1.1.3 排障铁律
99% 的高并发 RAG 雪崩故障,均源于 LLM 队列堆积与 KV Cache 资源耗尽。
当告警响起时,优先排查 LLM 层的三个指标:推理队列长度 → KV Cache 占用率 → P99 延迟。大部分情况下,根因在这三个指标之一。
配合分级告警策略:
- 预警级(P99/P50 比值 > 2、KV Cache > 75%):推送告警通知,运维关注
- 紧急级(P99/P50 比值 > 5、队列长度 > 100、超时率 > 5%):自动触发预设自愈策略(缩批、限流长请求、动态扩容)
1.2 防护二:LLM 网关层 Token 精细化限流
1.2.1 核心原理:从"限制请求次数"到"限制算力消耗"
上篇 2.1 节已经论证了传统 RPS 限流在 RAG 场景的失效机制。这里直接给出大厂统一方案:
以请求总 Token 数(输入 Token + 预估输出 Token)作为资源消耗的唯一度量标准,通过令牌桶算法在 LLM 专属网关层完成精细化流量管控。
1.2.2 落地全流程
① Token 预计算
请求抵达 LLM 网关后,调用 tiktoken 等轻量化分词工具,实时解析用户 Query + 检索上下文的 Input Token 数量。再结合业务场景预设的最大 Output Token 上限,两者相加得到本次请求预估总消耗 Token。
import tiktoken
def estimate_request_tokens(query: str, contexts: list[str], max_output_tokens: int = 512) -> int:
"""
LLM 网关层 Token 预计算
"""
enc = tiktoken.get_encoding("cl100k_base") # 或模型的对应 tokenizer
# 构造完整 Prompt 模板
system_prompt = "你是一个专业的知识问答助手,请根据提供的上下文回答问题。"
context_text = "\n\n".join(contexts)
full_prompt = f"{system_prompt}\n\n上下文:\n{context_text}\n\n问题:{query}\n\n回答:"
input_tokens = len(enc.encode(full_prompt))
total_estimated = input_tokens + max_output_tokens
return total_estimated
计算逻辑在网关层完成,耗时通常 < 5ms,不会引入额外的可感知延迟。
② 分布式令牌桶 — Redis + Lua 原子操作
以用户 ID、租户 ID、客户端 IP 为隔离维度,基于 Redis 集群搭建全局分布式令牌桶。核心要求是 Token 扣减的原子性——多网关节点并发扣减同一用户的配额时不能出现竞态问题。
-- Redis Lua 脚本:分布式令牌桶原子性 Token 扣减
-- KEYS[1]: 用户令牌桶 Key (例如 "token_bucket:user:123")
-- ARGV[1]: 本次请求预估 Token 数
-- ARGV[2]: 令牌桶容量(最大 Token 数)
-- ARGV[3]: 令牌填充速率(Tokens/秒)
-- ARGV[4]: 当前时间戳(毫秒)
local bucket_key = KEYS[1]
local requested = tonumber(ARGV[1])
local capacity = tonumber(ARGV[2])
local fill_rate = tonumber(ARGV[3])
local now = tonumber(ARGV[4])
-- 获取当前桶状态
local tokens = tonumber(redis.call('HGET', bucket_key, 'tokens'))
local last_refill = tonumber(redis.call('HGET', bucket_key, 'last_refill'))
-- 初始化令牌桶
if tokens == nil then
tokens = capacity
last_refill = now
end
-- 计算需要补充的令牌数
local elapsed_ms = now - last_refill
local refill_tokens = math.floor((elapsed_ms / 1000) * fill_rate)
tokens = math.min(capacity, tokens + refill_tokens)
-- 判断并扣减
if tokens >= requested then
tokens = tokens - requested
redis.call('HMSET', bucket_key, 'tokens', tokens, 'last_refill', now)
redis.call('EXPIRE', bucket_key, 3600) -- 1 小时过期
return {1, tokens} -- 放行,返回剩余 Token
else
redis.call('HMSET', bucket_key, 'tokens', tokens, 'last_refill', now)
return {0, tokens} -- 拒绝,返回当前 Token
end
③ 请求放行与拦截
- 配额充足 → 正常放行至推理队列
- 配额不足 → 网关直接返回 HTTP 429,拒绝请求进入 LLM 集群
- 这避免了无效请求在推理队列中排队占用资源(排队本身也消耗 KV Cache 预占)
④ 动态配额弹性调度
令牌桶的填充速率不是固定值,而是结合全链路监控的实时负载、时间段、业务高低峰动态调整:
| 时段 | 策略 | 目的 |
|---|---|---|
| 业务低峰期(凌晨、周末) | 自动放大 Token 配额 | 充分利用闲置 GPU 资源 |
| 流量高峰期(交易时段、大促) | 自动收缩配额 | 优先保障核心业务稳定 |
| 核心租户 / 内部运维 | 独立高配额白名单 | 保障关键服务可用性 |
1.2.3 效果评估
与传统 RPS 限流相比,Token 精细化限流的核心价值:
- 算力公平分配:杜绝单用户、单条超长请求独占集群资源,异构请求按实际算力消耗公平排队
- 源头拦截:限流拦截点前置到网关层,无效请求不进入推理队列,避免队列无限堆积
- 利用率提升:集群 GPU 资源利用率可提升 50% 以上,P99 延迟抖动显著降低
1.3 防护三:Continuous Batching 深度调优
这是全篇技术含金量最高的章节——也是区分"会用 vLLM"和"理解推理引擎调度机制"的分水岭。
1.3.1 Continuous Batching 的基本原理
传统 Static Batching 的问题
在 Static Batching 模式下,推理框架将一个固定大小的 Batch(比如 8 个请求)一起送入 GPU 做前向推理。GPU 必须等这 8 个请求全部完成后才能释放批次、处理下一批。
问题显而易见:如果 Batch 里有 1 个请求输出 2000 Token、7 个请求输出 20 Token,那 7 个短请求在 20 Token 后就完成了——但 GPU 闲置等它们出队,直到最长请求跑完。GPU 利用率常年只有 20%-40%。
Continuous Batching 的核心创新
Continuous Batching(连续批处理)由 vLLM、TGI(Text Generation Inference)等主流推理框架实现,核心创新有两点:
- 调度粒度从"请求级"细化到"Token 迭代级":不再等整个 Batch 完成,而是每生成一个 Token 后立即检查是否有请求完成、是否有新请求入队——完成的请求立即释放资源,新请求立即加入下一轮迭代
- PagedAttention 分页 KV Cache:类似操作系统的虚拟内存管理,将 KV Cache 拆分为固定大小的"页",请求的上下文以页为单位分配和回收,大幅减少显存碎片
效果:相较于传统 Static Batching,Continuous Batching 可将 GPU 利用率从 20%-40% 提升至 75%-90%,理论集群吞吐量提升 3-5 倍。
1.3.2 长尾延迟的根源:KV Cache 独占性引发的木桶效应
绝大多数团队仅开启 Continuous Batching 的基础功能,不做深度调优,会触发严重的 长尾延迟(Tail Latency),成为 1000QPS 高并发下接口超时的核心诱因。
根因分析
问题出在 KV Cache 的独占性上。当一个请求进入 Decode(解码生成)阶段后,它对应的历史上下文 Key、Value 向量会持续占用分页显存。必须等该请求完整生成所有 Token、主动退出批次后,对应的 KV Cache 分页才会被系统回收、重新分配。 推理框架无法中途强制抢占或释放单个请求的 KV Cache。
当线上长短请求混在一个 Batch 中时,触发木桶效应:
具体过程:Batch 内的短请求可能仅用几百毫秒就完成全部 Token 生成,逻辑上已经结束任务。但由于同 Batch 内存在尚未完成的超长请求,其占用的 KV Cache 无法整体释放,已完成的短请求只能被动等待——等 Batch 内耗时最长、生成 Token 最多的请求彻底结束。
更致命的是,在 1000QPS 以上脉冲式高并发场景下,多个长请求同时进入不同 Batch,会形成大面积 Batch 阻塞,队列层层堆积,长尾延迟从秒级恶化至数十秒,最终引发接口大面积超时与服务雪崩。
这解释了为什么很多团队开启 Continuous Batching 后,集群吞吐上涨,但线上超时、用户反馈卡顿问题反而增多。
1.3.3 根治方案:四位一体的长尾延迟治理体系
① GPU 算力资源池物理隔离(治本方案,优先级最高)
这是从架构层面切断长短请求互相干扰的核心手段。
集群拆分规划:
| 资源池 | 面向场景 | GPU 选型 | 集群占比 | 设计目标 |
|---|---|---|---|---|
| 短请求低延迟池 | FAQ、常规问答、短文本检索 | L4、A10G(中低端高吞吐) | 70%-80% | 高并发、低延迟 |
| 长请求高算力池 | 长文档解析、财报汇总、多文档对比 | A100 80GB、H100(大显存) | 20%-30% | 大上下文承载,容忍合理延迟 |
前置智能路由:
在 LLM 接入网关层集成 Token 计数器,请求进入推理集群前预计算 Input Token 总量,结合业务场景标签做分流:
# LLM 网关层智能路由逻辑
def route_to_pool(query: str, contexts: list[str],
short_pool_threshold: int = 512) -> str:
"""
基于 Token 预计算实现长短请求智能路由
"""
enc = tiktoken.get_encoding("cl100k_base")
full_prompt = build_prompt(query, contexts)
input_tokens = len(enc.encode(full_prompt))
if input_tokens <= short_pool_threshold:
return "short-request-pool" # 路由至短请求低延迟池
else:
return "long-request-pool" # 路由至长请求高算力池
池内独立调度: 两套资源池配置独立的 max_num_seqs、max_num_batched_tokens、限流规则和告警阈值,互不影响。
② 请求内容约束:限制单请求资源上限
针对极端超长请求,从请求本身做约束:
- 全局 Token 硬限制:短请求池严格限制输入/输出 Token 上限;长请求池放宽但有封顶值。网关层拦截超出阈值的恶意/异常请求,直接返回 429
- 自动分片处理:对于超长输入文本,系统自动语义分片 → 拆分为多个子请求 → 分批推理 → 结果语义拼接,替代单次超大请求
- 优雅截断:生成阶段触发 Output Token 上限时,执行优雅截断并追加标准化提示文案(如"因篇幅限制,以上为回答的核心内容"),而非强制中断
③ 动态批次参数自适应
基于集群实时负载,动态调整 vLLM/TGI 的核心调度参数。
核心参数(以 vLLM 为例):
# vLLM 动态批次参数配置示例
from vllm import LLM, SamplingParams
from vllm.config import SchedulerConfig
def get_dynamic_scheduler_config(current_qps: float, gpu_utilization: float) -> dict:
"""
基于实时负载动态调整批次参数
"""
if gpu_utilization > 0.85 or current_qps > 600:
# 高峰期策略:缩批次,优先保 P99 延迟
return {
"max_num_seqs": 16, # 单 Batch 最大请求数(缩小)
"max_num_batched_tokens": 2048, # 单 Batch 最大 Token 总数(缩小)
"max_waiting_time_ms": 50, # 批次等待窗口(缩短,避免请求聚集)
}
elif gpu_utilization < 0.5:
# 低峰期策略:扩批次,最大化吞吐
return {
"max_num_seqs": 64, # 放大 Batch 尺寸
"max_num_batched_tokens": 8192, # 放大 Token 容量
"max_waiting_time_ms": 200, # 延长等待窗口,提高组批效率
}
else:
# 常规策略
return {
"max_num_seqs": 32,
"max_num_batched_tokens": 4096,
"max_waiting_time_ms": 100,
}
# 初始化 vLLM 时注入配置
config = get_dynamic_scheduler_config(current_qps=450, gpu_utilization=0.72)
llm = LLM(
model="Qwen/Qwen2-7B-Instruct",
max_num_seqs=config["max_num_seqs"],
max_num_batched_tokens=config["max_num_batched_tokens"],
)
策略总结:
| 场景 | Batch Size | 等待窗口 | Token 上限 | 优化目标 |
|---|---|---|---|---|
| 高峰期(QPS > 600) | 缩小(16) | 缩短(50ms) | 降低(2048) | 优先保障 P99 |
| 低峰期(利用率 < 50%) | 放大(64) | 延长(200ms) | 提高(8192) | 榨取 GPU 算力 |
| 常规 | 适中(32) | 适中(100ms) | 适中(4096) | 平衡 |
④ 长尾指标监控 + 自动自愈
这是最后一道兜底防线——前三个方案未完全覆盖的边缘场景,靠自动监控和自愈来收口。
核心监控指标:P99/P50 延迟比值
这个单一指标能非常准确地反映"长尾"问题的严重程度:
- 正常集群:P99/P50 ≤ 2(因为即使是最慢的请求,也只比中位数请求慢 1 倍)
- 长尾严重:P99/P50 > 5(少量的极端长请求将 P99 拉高到中位数的 5 倍以上)
- 即将雪崩:P99/P50 > 10
自动干预动作队列(按优先级顺序触发):
- 临时缩小 Batch Size(降低木桶效应的影响面)
- 对长请求 Token 流量执行更严格的限流
- 触发 GPU 节点自动扩容
- 清空低优先级请求队列
# 长尾监控 + 自动自愈的伪代码逻辑
TAIL_RATIO_THRESHOLD_WARN = 2.0 # 预警阈值
TAIL_RATIO_THRESHOLD_CRIT = 5.0 # 紧急阈值
def check_tail_latency_and_heal(metrics: dict):
"""
基于 P99/P50 比值的长尾自愈检测
"""
p50 = metrics.get("llm_p50_latency_ms", 0)
p99 = metrics.get("llm_p99_latency_ms", 0)
ratio = p99 / p50 if p50 > 0 else 0
if ratio > TAIL_RATIO_THRESHOLD_CRIT:
# 紧急级:自动触发干预
trigger_action("shrink_batch_size") # ① 缩批
trigger_action("throttle_long_requests") # ② 限制长请求
trigger_action("scale_out_gpu_nodes") # ③ 扩容 GPU
alert("critical", f"P99/P50={ratio:.1f}, 自动自愈已触发")
elif ratio > TAIL_RATIO_THRESHOLD_WARN:
alert("warning", f"P99/P50={ratio:.1f}, 长尾延迟正在恶化")
1.4 防护四:三级分级缓存体系
1.4.1 核心矛盾:性能 vs 数据一致性
缓存是 RAG 架构中性价比最高的高并发优化手段——无需新增 GPU、CPU 算力,即可拦截 30%-60% 的重复请求,大幅降低全链路各环节压力。
但 RAG 缓存存在天然的数据一致性风险:知识库文档更新、业务规则变更后,老旧缓存会输出脏数据、过时答案,引发模型幻觉和业务错误。
一刀切的方案行不通:
- 全量禁用缓存 → 性能浪费,每次请求都要走完四段完整链路
- 无差别全量缓存 → 数据失真风险极高
大厂的解决方案是:按缓存层级和风险等级做差异化管控——三级分级缓存,每一级有独立的缓存内容、风险等级、管理策略。
一级缓存:Query-Embedding 向量缓存(零风险,全量开启)
缓存内容:用户原始查询文本(Query)对应的 Embedding 向量化结果。
为什么零风险:向量结果由"固定文本 + 固定版本 Embedding 模型"唯一确定。只要查询内容和模型版本不变,向量永远不会改变。与后端知识库是否更新完全无关——这是纯计算结果的确定性缓存,没有任何一致性问题。
落地规则:
- 缓存 Key:
SHA256(Query + Embedding模型版本) - 存储层级:进程内存 LRU(一级热数据)+ Redis 分布式存储(二级温数据)
- TTL:24 小时标准过期,自动淘汰长期不访问的冷数据
- 淘汰策略:LRU(Least Recently Used),内存满时淘汰最久未访问的条目
效果:对 FAQ 类高频重复查询场景,缓存命中率可达 80%+,大幅削减 Embedding 层的 CPU 计算开销。
二级缓存:检索 + Rerank 结果缓存(中风险,版本化管控)
缓存内容:向量库 ANN 检索 + Rerank 重排后筛选出的 TopK 高相关文档片段、相似度分数、文档来源元数据。
为什么中风险:这部分数据完全依赖底层知识库。当知识库执行 ETL 全量更新、文档增量同步、单篇内容修改后,原有检索结果会失效——新文档片段未被缓存覆盖,旧缓存返回已删除/已修改的片段。
核心方案:版本号驱动的自动失效机制
import hashlib
import json
from typing import List, Optional
import redis
class RAGCacheV2:
"""
二级缓存:检索 + Rerank 结果缓存(版本化管控)
"""
def __init__(self, redis_client: redis.Redis):
self.redis = redis_client
self.default_ttl = 3600 # 兜底 TTL 1 小时
def cache_key(
self,
query: str,
kb_version: int, # 知识库全局自增版本号
top_k: int = 5,
) -> str:
"""
缓存 Key 设计:query_hash + kb_version + TopK
知识库更新 → 版本号自增 → 旧 Key 自动不匹配 → 旧缓存天然失效
"""
query_hash = hashlib.sha256(query.encode()).hexdigest()[:16]
return f"rag:retrieval:{query_hash}:v{kb_version}:top{top_k}"
def get_retrieval_result(
self, query: str, kb_version: int, top_k: int = 5
) -> Optional[List[dict]]:
key = self.cache_key(query, kb_version, top_k)
cached = self.redis.get(key)
if cached:
return json.loads(cached)
return None
def set_retrieval_result(
self, query: str, kb_version: int, result: List[dict], top_k: int = 5
):
key = self.cache_key(query, kb_version, top_k)
self.redis.setex(key, self.default_ttl, json.dumps(result, ensure_ascii=False))
def invalidate_by_doc_id(self, doc_id: str):
"""
通过 MQ 消息触发精准失效:文档局部更新时,
删除包含此 doc_id 的所有缓存条目
(实际实现中建议使用 Redis SCAN + 模式匹配或布隆过滤器)
"""
# 简化示例:实际生产环境可使用布隆过滤器追踪 Query → 缓存 Key 映射
pass
知识库版本号管理机制:
- 为每一套知识库分配全局自增版本号(在 DB 中维护)
- 每次 ETL 全量更新或文档增量同步后,版本号自动递增
- 旧版本缓存因 Key 不匹配(
v{old}≠v{new}),天然自动失效 - 搭配 MQ 消息队列:文档局部更新时,推送事件 → 消费端精准删除关联 Query 的缓存 Key
效果:降低向量数据库 30% 以上查询压力,同时通过版本号机制彻底解决知识库更新后的脏数据问题。
三级缓存:Query-Answer 最终答案缓存(高风险,白名单管控)
缓存内容:LLM 模型最终生成的完整问答答案,是全链路最终输出。
为什么高风险:答案受三层因素共同影响——知识库内容 + LLM 模型版本 + 业务规则——任何一层发生变化,缓存答案都可能过期失效。一旦命中脏缓存,会直接输出错误答案给最终用户,影响核心业务。
管控策略(严禁全局开启):
| 维度 | 策略 |
|---|---|
| 适用范围 | 仅对静态 FAQ、固定业务规则、长期不变的知识库内容白名单开放 |
| 禁用场景 | 动态行情、实时研报、临时政策、时效性内容 — 一律禁用 |
| TTL 策略 | 短 TTL(5-15 分钟),最小化脏数据存活窗口 |
| 主动失效 | MQ 消费知识库变更事件 → 精准删除关联 Query 的缓存 Key |
| 版本标记 | 缓存值附带知识库版本号 + 模型版本号,对比校验 |
| 熔断机制 | 若检测到知识库近期有更新,对该知识库的答案缓存自动降级为"透传"模式 |
二、架构师的核心认知升级
2.1 “算法定上限,工程定下限”
RAG 系统能否稳定承载 1000QPS 峰值流量,80% 取决于工程治理能力,20% 取决于模型本身的能力。
结合上篇的四段式管线性能模型和下篇的四道防护体系,这套方法论的核心逻辑可以概括为:
- 全链路分段可观测 → 从"盲人摸象式全局扩容"变成"外科手术式精准优化"
- Token 精细化流量管控 → 从"误伤式 RPS 限流"变成"算力公平的 Token 配额管控"
- Continuous Batching 深度调优 → 从"开了就行"变成"根据负载动态调整的长尾根治方案"
- 三级分级缓存 → 从"全量缓存脏数据"或"全量禁用浪费性能"的二选一,变成"按风险等级精细管控"
这四道防护环环相扣,构成了一个完整的闭环治理体系。
2.2 上线前的算力精算
多数 RAG 服务上线后频繁出现峰值崩溃、资源过载、负载失衡等问题,核心根源是上线前缺乏量化算力评估,仅依靠经验配置硬件——要么资源过剩成本浪费,要么资源不足承载不足。
标准化算力精算流程:
算力评估公式:
所需 GPU 数量 = (峰值 QPS × 平均每次请求总 Token 数) / (单卡 Token 生成速度 × 目标利用率) × 冗余系数
示例:
峰值 QPS = 800
平均每次请求总 Token = 512 (input) + 256 (output) = 768
单卡 A100 Token 生成速度 ≈ 2000 tokens/s
目标 GPU 利用率 = 0.8
冗余系数 = 1.3
所需 GPU = (800 × 768) / (2000 × 0.8) × 1.3
= 614,400 / 1,600 × 1.3
= 384 × 1.3
≈ 500(取值 50 张 A100)
实际部署时建议分两池:
- 短请求池(70%):35 张 A10/L4,面向低延迟高并发
- 长请求池(30%):15 张 A100 80GB,面向大上下文
2.3 写在最后
拿到 1000QPS 这个需求后,一个成熟架构师的思考路径应该是:
- 先分段看:四个阶段各自的性能上限是多少?1000QPS 下哪个环节是瓶颈?
- 再做限流:用什么维度做限流?RPS 还是 Token?限流点放在哪里?
- 再调调度:Continuous Batching 的参数怎么设?长短请求怎么隔离?
- 最后加缓存:哪些能缓存?哪些不能?各自的风险等级和管控策略是什么?
- 上线前精算:做完以上优化后,实际需要多少 GPU?冗余留多少?
真正的竞争力不在于你知道 Continuous Batching 或 Token 限流这些方案的名字,而在于你知道在哪个环节投入哪种资源、在哪个维度做哪种限流、在哪个地方做哪种缓存——每一步都有数据支撑,每一个决策都有架构逻辑。
附录:关键参数速查表
| 阶段 | 吞吐上限(单节点/卡) | P99 延迟 | 核心瓶颈 | 扩容成本 | 优先优化手段 |
|---|---|---|---|---|---|
| Embedding | 3000-5000 QPS | ≤ 20ms | CPU | 极低 | 模型量化、高频缓存 |
| 向量检索 | 500-1000 QPS | ≤ 80ms | 内存/IO | 低 | IVF-PQ 索引、混合检索 |
| Rerank | 30-150 QPS | ≤ 200ms | GPU 计算 | 中 | TensorRT 加速、动态 Batch |
| LLM 生成 | 3-15 QPS | 1-5s+ | KV Cache/显存 | 极高 | 资源池隔离、Token 限流、批处理调优 |
| 限流维度 | 适用场景 | RAG 是否适用 | 原因 |
|---|---|---|---|
| RPS 限流(Nginx limit_req) | 传统 Web 接口(请求同构) | ❌ 不适用 | 无法感知请求异构,轻重请求权重相同 |
| Token 令牌桶(Redis + Lua) | RAG / LLM 推理场景 | ✅ 适用 | 以实际算力消耗为度量,公平分配资源 |
| 缓存层级 | 风险等级 | 开启策略 | TTL | 失效机制 |
|---|---|---|---|---|
| Embedding 向量缓存 | 零风险 | 全量开启 | 24h | LRU 自动淘汰 |
| 检索 + Rerank 结果缓存 | 中风险 | 全量开启 + 版本管控 | 1h 兜底 + 版本失效 | 知识库版本号自增 |
| 最终答案缓存 | 高风险 | 白名单管控 | 5-15min + MQ 主动清除 | 版本校验 + 事件驱动 |

306

被折叠的 条评论
为什么被折叠?



