尨植博客网站性能优化实战|图片压缩96%、Nginx直出、Gzip压缩与数据库修复全记录

🔧 智能硬件研发日志 📅 2026-07-02
性能优化 WebP Nginx Gzip SQLAlchemy 网站加速 图片压缩

一、问题发现与诊断

用户反馈博客首页、对话页面加载缓慢(5-10秒才出内容),通过 Chrome DevTools Network 面板 + Nginx 日志 + Docker 后端日志三层排查,确认 5 个核心瓶颈:

#问题严重度
112张PNG图片共18.7MB,首页加载10张=17MB严重
2静态图片通过FastAPI后端代理,未由Nginx直接serve严重
3proxy_buffering off 全局关闭,HTML无Gzip压缩中等
4SQLAlchemy连接泄漏,后端日志频繁报GC清理错误中等
5聊天页5个API串行请求(personality被重复调用2次)中等
其中图片是绝对瓶颈——首页仅图片就加载 17MB,在 4G 网络下需要 5-8 秒。

二、修复 #1:WebP 图片压缩(18.7MB → 0.8MB,缩减96%)

使用 Python Pillow 库将 12 张 PNG 图片批量转换为 WebP 格式:

from PIL import Image
for png in png_files:
    img = Image.open(png).convert('RGB')
    img.save(webp_path, 'WEBP', quality=80, method=6)

参数说明:quality=80 平衡画质与体积,method=6 使用最高压缩算法(速度最慢但体积最小)。

指标压缩前(PNG)压缩后(WebP)缩减
12张总大小18.7 MB0.8 MB96%
单张大小范围1.3 ~ 2.1 MB43 ~ 123 KB94~97%
首页图片总载荷~17 MB~470 KB97%

同步更新了全部 8 个 HTML 模板中的图片引用(.png.webp),包括 Jinja2 模板变量路径。

三、修复 #2:Nginx 直接 Serve 静态文件

之前静态文件先上传到 FastAPI 后端再响应,增加了不必要的 Python 层开销:

# docker-compose.yml 新增 volume 挂载
nginx:
  volumes:
    - ./backend/app/blog/static:/usr/share/nginx/static/blog:ro

# nginx conf 新增 alias 指令
location /blog/static/ {
    alias /usr/share/nginx/static/blog/;
    expires 7d;
    add_header Cache-Control "public, immutable";
}

效果:图片直接从 Nginx 返回(image/webp,延迟从 200-500ms 降至 ~13ms),同时设置 7 天浏览器缓存(immutable),回访用户零网络请求。

四、修复 #3-#5:Gzip、连接池、API去重

修复 #3 — HTML Gzip 压缩:之前的 proxy_buffering off 是全局设置(为了支持 SSE 流式聊天),但这导致所有 HTML 页面无法开启 Gzip。修复后改为按路径区分:默认 proxy_buffering on(自动 Gzip),仅 /api/v1/chat/chat 设置为 proxy_buffering off
效果:blog 首页 52KB → 12KB (76%),chat 页面 90KB → 23KB (74%)。


修复 #4 — SQLAlchemy 连接泄漏:database.py 中的 get_db_safe() 从普通 async 函数改为 generator 模式:

async def get_db_safe():
    session = _raw_session_factory()
    try:
        yield session
        await session.commit()
    except Exception:
        await session.rollback()
    finally:
        await session.close()  # 确保连接归还池子

finally: session.close() 确保无论正常或异常,数据库连接都能归还连接池,消除了后端日志中频繁出现的 "non-checked-in connection" GC 错误。


修复 #5 — 聊天页 API 去重:发现 loadMood()loadPersonality() 都调用同一个 /api/v1/chat/personality/{id} 接口,将人格标签渲染逻辑合并到 loadMood() 内,selectPot() 的并行请求从 4 个减少为 3 个。

五、最终效果对比

指标修复前修复后提升
首页图片总载荷17 MB (10张PNG)~470 KB (10张WebP)36x
图片加载延迟FastAPI代理 200-500msNginx直出 ~13ms15-38x
首页 HTML 大小52 KB (无压缩)12 KB (Gzip 76%)4.3x
DB连接泄漏频繁GC错误已消除-
聊天页API请求4个(含1重复)3个(并行)-25%

整体效果:首页总传输量从 ~17MB 降至 ~500KB,首屏可交互时间从 5-10 秒降至 1 秒以内,在移动 4G 网络下体验提升尤为显著。

六、技术沉淀

  1. WebP 是 Web 场景的图片最优解:相同画质下体积仅为 PNG 的 3-4%,主流浏览器 100% 支持,无兼容性顾虑。
  2. Nginx 做静态文件第一入口:不要让 FastAPI/Python 代理静态资源,Nginx 的 sendfile 零拷贝机制效率远超应用层。
  3. proxy_buffering 要按路径区分:SSE 需要 off,但普通页面 on 才能启用 Gzip。一个 location 一个策略,不要一刀切。
  4. async generator 是 SQLAlchemy 的正确打开方式:try/yield/finally 模式确保异常时也能关闭 session,避免连接池耗尽。
研发调试中
持续迭代
本文为个人原创实验记录,版权归作者所有,禁止商用转载。
如需技术交流,欢迎通过博客留言或邮件联系。
← 返回硬件专栏 返回首页 →

💬 技术交流

📋 留言规则: 本留言板仅用于技术学习交流,严禁发布广告、外部引流链接、涉政、色情、赌博、 营销推广及一切违法违规内容。本站为个人非经营性网站,不提供任何商业接单、付费咨询服务。 所有留言需经人工审核后展示。

📧 技术交流邮箱:

留言提交功能将在公安网安备案全部通过后开放,感谢理解。

💬 过往技术交流

加载中...
AI 对话