FastAPI接口用Redis做动态缓存应自定义装饰器而非依赖第三方库,核心是构造唯一key(含path、method、排序后的query参数及用户标识)、JSON/msgpack序列化返回值(兼容datetime/Pydantic)、捕获Redis异常降级、分级设置TTL并配合主动清理保障一致性。

FastAPI接口怎么用Redis做动态缓存
直接结论:不依赖fastapi-cache这类第三方封装,自己用redis-py + 自定义装饰器更可控,尤其当需要按请求参数、Header或用户身份差异化缓存时。
核心逻辑是拦截请求 → 生成唯一缓存key → 先查Redis → 命中则反序列化返回 → 未命中则执行原函数 → 序列化写入Redis。关键在key怎么构、数据怎么序列化、过期时间怎么设。
常见错误现象:cache miss率高(key构造没包含query参数)、500 Internal Server Error(返回值含datetime或pydantic.BaseModel未序列化)、缓存击穿(高并发下大量请求同时穿透到后端)。
自定义装饰器里怎么拼Redis key
key必须能区分不同请求语义,否则A用户看到B用户的缓存结果。不能只用路径,要组合:path + method + sorted(query_params.items()) + 可选的user_id或Authorization头哈希。
立即学习“Python免费学习笔记(深入)”;
实操建议:
- 用
hashlib.md5把拼接字符串转成固定长度key,避免超长key触发Redis警告 - 不要直接用
str(request.query_params),它顺序不稳定;改用tuple(sorted(request.query_params.items())) - 如果接口支持
Accept: application/json和application/msgpack两种格式,key里也得带上accept头 - 示例key生成片段:
key = f"fastapi:{method}:{path}:{md5((str(sorted_q) + str(user_hash)).encode()).hexdigest()}"
为什么不能直接pickle.dumps()缓存返回值
因为FastAPI路由函数返回的通常是dict、pydantic.BaseModel实例、甚至StreamingResponse,而pickle有安全风险(反序列化任意代码)、跨Python版本不兼容、且无法被其他语言服务读取。
推荐方案是统一走JSON序列化,但要注意:
-
json.dumps()默认不支持datetime、Decimal、UUID—— 必须传default=str或自定义JSONEncoder -
pydantic.BaseModel建议调用.model_dump()(v2)或.dict()(v1),别直接json.dumps(obj) - 如果性能敏感(如高频小数据),可用
msgpack.packb(obj, default=str)替代JSON,体积更小、速度更快 - 缓存value建议加个简单版本标记,比如
{"v": "1.0", "data": ...},方便后续格式升级时做兼容处理
装饰器里怎么处理缓存异常和降级
Redis挂了不能让整个接口不可用。装饰器必须捕获ConnectionError、TimeoutError等,并静默跳过缓存逻辑,直通业务函数。
实操要点:
- 用
try/except (redis.ConnectionError, redis.TimeoutError)包裹所有Redis操作,不要捕获Exception - 设置
cache_ttl参数时,避免硬编码,可按接口重要性分级:列表页60秒,详情页300秒,配置类接口3600秒 - 对高频写+低频读的接口(如用户个人资料),考虑加
cache_buster逻辑:当更新接口被调用时,主动delete对应key - 别忘了给装饰器加
@functools.wraps(func),否则FastAPI的依赖注入、文档生成会出问题
最易被忽略的是缓存一致性边界——比如用户修改了资料,但缓存没删,或者删了却因网络问题失败。这时候与其强一致,不如接受几秒延迟,靠TTL兜底,再配合业务侧主动清理作为优化手段。


















