惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

U
Unit 42
F
Fortinet All Blogs
Martin Fowler
Martin Fowler
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
IT之家
IT之家
Blog — PlanetScale
Blog — PlanetScale
罗磊的独立博客
V
V2EX
Vercel News
Vercel News
Apple Machine Learning Research
Apple Machine Learning Research
J
Java Code Geeks
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
T
Tailwind CSS Blog
I
InfoQ
Engineering at Meta
Engineering at Meta
WordPress大学
WordPress大学
S
SegmentFault 最新的问题
H
Help Net Security
腾讯CDC
D
Docker
P
Proofpoint News Feed
GbyAI
GbyAI
博客园 - 三生石上(FineUI控件)
aimingoo的专栏
aimingoo的专栏

博客园 - 一名程序媛呀

Anki插件开发必知必会:钩子函数与右键菜单定制 httpx 传参总报错?这次把 GET、POST、文件上传到响应处理的坑给你一次填平 从卡顿到丝滑:FastAPI 调用外部 API 的正确姿势(httpx 实战) 还在 XHR、Fetch 和 Axios 之间纠结?我踩过的坑,希望你一个都不用碰到 你的REST接口还在“过度投喂”数据吗?——FastAPI + GraphQL实战避坑指南 Uvicorn、Gunicorn 傻傻分不清?FastAPI 生产部署避坑指南 Termux里的二进制和脚本,到底怎么运行才不踩坑?Termux-service 保活妙招! 刚部署的 LibreTranslate 频频翻车?我掏出了 20 年前的 StarDict 词典,用 FastAPI 搭了个本地词典翻译 API 别再用网页翻译看源码了!你的私人翻译神器LibreTranslate,部署避坑指南来了 掏出手机就能搭个 WebDAV 同步服务器?这操作有点香 别只盯着GitBook了!这个文档神器让你的笔记秒变网站 写爬虫时用了代理还被封?Python 代理的那些隐藏坑,我替你踩明白了 FastAPI 身份验证总踩坑?这份 FastAPI Users “避坑指南”请收好 旧手机别扔!用 Termux 搭个私人云盘,比网盘香多了 你的FastAPI又在服务器上“跑不起来”了?来,今天咱把打包这件事彻底聊透 写页面时别再把 Element Plus 整个搬进来啦!Vue3按需加载的坑我帮你踩平了 前端包管理咋选?我从npm叛逃到pnpm的血泪史(附避坑指南) 聊聊 fetch 使用中我踩过的那些坑和正确打开方式 FastApiAdmin 后端接口开发好了,前端管理界面怎么调用与显示? 给 FastApiAdmin 加个“会议纪要”模块,我把后端二次开发的坑踩了个遍 我用了FastApiAdmin后,连夜把踩过的坑都整理出来了 告别 Typora 后的新欢:我把所有笔记迁移到了 Obsidian 这个“第二大脑” 你的Agent API还在裸奔?从认证到沙箱,我用FastAPI搭了几道防线 让 FastAPI Agent 思考不阻塞:手把手教你实现异步任务与后台处理方案 让FastAPI Agent真正记住你:聊聊会话记忆与持久化存储的落地实践 FastAPI Agent 函数调用实战:我让 AI 学会了“自己动手查天气“ 初探:用 FastAPI 搭建你的第一个 AI Agent 接口 FastAPI 少有人提的实用技巧:把 Depends 依赖提到路由层,代码少写60% FastAPI 生产环境静态文件完全指南:从 /favicon.ico 404 到 HSTS 混合内容,一次全根治 用了loguru我才明白,Python日志还能这么写
FastAPI + PostgreSQL 实战:给应用装上“缓存”和“日志”翅膀
一名程序媛呀 · 2026-03-11 · via 博客园 - 一名程序媛呀

一次搞定性能瓶颈与故障排查难题,让API飞起来,日志不再乱。

👉 咱们今天聊这些:

🚨 痛点直击 ⚙️ 原理速通 💻 代码实战 🔥 踩坑血泪 🚀 进阶姿势

1. 你的API还好吗? 🎯

有个朋友跟我吐槽,说他们的FastAPI服务上线第三天,数据库连接直接爆满,接口响应从50ms飙升到3秒。我问他:“你们用缓存了吗?” 他一脸懵:“数据库不是挺快的吗?”

你看,这不就是典型的“裸奔上阵”吗?PostgreSQL再强,也扛不住高频重复查询的“暴击”。而且一旦出问题,查日志全靠grepawk,累死个人。

今天我们就来解决这两个痛点:用Redis给数据库加个“小本本”缓存热点数据,用Elasticsearch让日志变成“可搜索的数据库”。全程实操代码,都是我亲自踩过的坑,包你少走弯路。

2. 核心原理:收银员与小本本

PostgreSQL想象成超市的总仓库,每次查询都得跑大老远去取货。Redis就是收银员随身带的小本本,记下最常卖的商品(热点数据)。下次顾客要,直接从小本本查,秒级响应。

Elasticsearch呢?它像个档案管理员,把所有日志分门别类建索引,你可以用关键词秒搜到任何请求的细节,再也不用登录服务器翻文件了。

3. 开干!FastAPI + Redis + ES 集成

假设你已经有个FastAPI项目,连了PostgreSQL。咱们一步步加料。

🔧 3.1 安装依赖

pip install redis elasticsearch[async] fastapi-cache2[redis]

这里用了fastapi-cache库,封装了缓存装饰器,省得自己写重复代码。当然你也可以直接用aioredis,看个人喜好。

📝 3.2 Redis缓存装饰器实战

创建一个缓存工具模块:

# app/cache.py
from fastapi_cache import FastAPICache
from fastapi_cache.backends.redis import RedisBackend
from fastapi_cache.decorator import cache
import redis.asyncio as redis

async def init_cache(redis_url: str = "redis://localhost:6379"):
    redis_client = redis.from_url(redis_url, encoding="utf-8", decode_responses=True)
    FastAPICache.init(RedisBackend(redis_client), prefix="fastapi-cache")

然后在启动事件中调用:

# main.py
from app.cache import init_cache

@app.on_event("startup")
async def startup():
    await init_cache()

重点来了:在需要缓存的接口上加@cache()装饰器:

@app.get("/items/{item_id}")
@cache(expire=60)  # 缓存60秒
async def get_item(item_id: int, db: Session = Depends(get_db)):
    # 这里是数据库查询
    item = db.query(Item).filter(Item.id == item_id).first()
    return item

就这么简单!同样的请求60秒内直接走Redis,数据库连看都不看一眼。

⚠️ 这里我踩过一个坑: 如果接口参数里有db session这种不可哈希的对象,fastapi-cache会报错。解决办法是把依赖项移到装饰器外面,或者用cache(…, key_builder=…)自定义键。

📜 3.3 Elasticsearch日志中间件

日志不能只打控制台,要统一送ES。我们写一个中间件,记录每次请求的方法、路径、状态码、耗时等。

# app/log_middleware.py
from elasticsearch import AsyncElasticsearch
import time
import json

es = AsyncElasticsearch(["http://localhost:9200"])

@app.middleware("http")
async def log_to_es(request: Request, call_next):
    start = time.time()
    response = await call_next(request)
    duration = time.time() - start

    log_data = {
        "method": request.method,
        "url": str(request.url),
        "status_code": response.status_code,
        "duration": round(duration, 4),
        "client_ip": request.client.host,
        "timestamp": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
    }

    # 异步发送日志,别阻塞请求
    await es.index(index="fastapi-logs", document=log_data)
    return response

注意: 生产环境千万别每个请求都同步发ES,得用批量发送+缓冲,或者用background tasks。上面只是demo,实际要优化。

4. 那些年我踩过的坑(必看)

集成完了?别急,下面这几个坑我挨个帮你排雷。

🔥 坑1:缓存穿透 — 查询一个不存在的id,每次都会穿透到数据库。解决方案:缓存空值或布隆过滤器。

🔥 坑2:ES索引爆炸 — 如果不管理索引生命周期,日志会把磁盘撑爆。一定要用ILM(索引生命周期管理)或定时删除旧索引。

🔥 坑3:异步Redis连接未关闭 — 服务停止时忘记关闭Redis连接,导致警告。在shutdown事件里加await FastAPICache.clear()redis_client.close()

🔥 坑4:ES连接失败导致请求阻塞 — 日志中间件里要加try-except,否则ES挂掉整个API也挂了。

是不是以为这样就完了?No no no,还有进阶操作:

✅ 用缓存预热,在启动时加载热门商品到Redis。

✅ 给ES日志加上APM trace ID,配合链路追踪。

✅ 把缓存装饰器封装成统一@custom_cache,自动处理异常和降级。

好了,今天就聊到这儿。如果你也在FastAPI里集成过Redis或ES,欢迎留言分享你的骚操作或者踩坑经历,咱们一起进步!

💡 觉得有用?点个赞+关注,下次写“FastAPI+celery异步任务实战”时第一个通知你!

—— 爱写代码也爱吐槽的一名程序媛