11.2 性能优化策略 本节摘要:调优不是凭感觉加缓存,而是一个闭环:压测定基线、定位瓶颈、单点修改、复测验证。本节用这个闭环处理墨迹博客的高峰慢响应:从压测工具与慢日志定位,到数据库层(索引、连接池)、应用层(查询、缓存命中率)逐层收口,最后给前端侧的免费提速项。 闭环先于技巧 没有基线的优化是玄学。墨迹博客的处理顺序: 压测工具用命令行负载工具即可:模拟 50 并发持续 30 秒,记录分位数。看 p95 而非平均——平均数会被快请求稀释,用户抱怨的永远是"最慢的那次"。 第 4 章写的那个耗时中间件此刻变成金矿:日志按耗时倒序,前几名一目了然——详情页 890ms、列表页 620ms、标签页 540ms。慢从哪来?
本节摘要:调优不是凭感觉加缓存,而是一个闭环:压测定基线、定位瓶颈、单点修改、复测验证。本节用这个闭环处理墨迹博客的高峰慢响应:从压测工具与慢日志定位,到数据库层(索引、连接池)、应用层(查询、缓存命中率)逐层收口,最后给前端侧的免费提速项。
没有基线的优化是玄学。墨迹博客的处理顺序:
1 定基线:压测工具对首页打出 p50/p95 响应时间与吞吐 2 找瓶颈:慢查询日志 + 应用日志(第4.3节中间件记录的耗时) 3 单点改:一次只改一处 4 复测:同参数压测对比 p95 5 记录:把结论写进项目文档,避免下个人重新摸索
压测工具用命令行负载工具即可:模拟 50 并发持续 30 秒,记录分位数。看 p95 而非平均——平均数会被快请求稀释,用户抱怨的永远是"最慢的那次"。
第 4 章写的那个耗时中间件此刻变成金矿:日志按耗时倒序,前几名一目了然——详情页 890ms、列表页 620ms、标签页 540ms。慢从哪来?三个来源按检查顺序排:数据库(查询数与耗时)、应用(模板渲染、循环里的重复计算)、外部(第三方接口阻塞)。
开慢日志。 数据库侧把超过 100 毫秒的查询记进慢日志:
# 应用侧等价做法:日志打印慢 SQL(开发与预发) LOGGING["loggers"]["django.db.backends"] = { "level": "DEBUG", "handlers": ["console"], }
详情页的问题当场现形:正文取回后模板又逐一取评论的评论者资料——第 3 章讲过的 N+1 在生产复现,select_related 一行修复,890ms 降到 120ms。上线前治理过的坑会被新代码重新引入,慢日志是常设的警报器。
核对索引。 标签页的全表扫描(第 3 章末尾提过的案子)用执行计划确认后补迁移加索引,540ms 到 9ms。索引纪律:贴着"过滤加排序"的实际形状建复合索引;每加一个索引写入就贵一分,不查的列别建。
连接池。 高并发下每请求新建数据库连接的开销可观,连接池复用长连接。应用侧配置持久连接是低成本起步,吞吐大的站再上进程外的池中间件。
模板里的隐性循环。 列表页 620ms 的构成:200ms 查询、400ms 渲染。翻模板发现每篇文章都调用了一个"计算阅读时长"的模型方法,方法内部又查了一次标签。解法是把计算挪进 annotate(数据库一次算完)——模板层零逻辑的信条(第 5 章)在性能上的回报。
缓存命中率。 加了缓存仍慢,先看命中率:Redis 的统计命令里命中率不过半,通常是键过期过短或键空间太散。命中率九成以上,缓存已尽其用,瓶颈在别处。
减对象序列化。 接口返回大列表时,只取需要的字段(values 或切片分页),既省数据库也省序列化时间。

后端收口后,用户体感的另一半在浏览器:开启 gzip(Nginx 一行配置,文本体积降七成);静态文件设长缓存头并启用内容指纹(文件名带哈希,改版才失效);图片按显示尺寸裁切、懒加载。这三项不动一行业务代码,移动端首屏常有翻倍改善。
还有一条贯穿性的纪律值得单独强调:性能是会退化的资产,不是一次性的工程。这次调优结束后三个月,墨迹博客又慢了回来——不是旧问题复发,而是三个月里新加的功能各自带进了两三条多余查询,单看每条都不严重,加起来就把首页拖回了五百毫秒。对策不是再打一次突击战,而是把度量常态化:给第 4 章的耗时中间件加一条阈值告警,任何请求超过一秒就记录;每周扫一眼慢日志,趁问题还小就修。把性能当成体重来日常管理,别当成心脏病来抢救,维护期的调优成本会低一个数量级。
读的路径收口了,还有一类慢它治不了——必须做但不能在请求里做的事。下一节交给队列。