一次Python内存泄漏的真实排查经历

上周线上服务内存占用一路飙升,从200MB涨到2GB,最后OOM被系统杀了。排查过程挺曲折的,分享一下,希望帮到踩类似坑的朋友。

现象

监控告警,服务内存持续增长,重启后从200MB开始,几小时后又到2GB。典型的内存泄漏特征。

排查过程

第一步:确认是不是真的泄漏

先用tracemalloc追踪内存分配:

python 复制代码
import tracemalloc
tracemalloc.start()

# ... 运行业务代码 ...

snapshot = tracemalloc.take_snapshot()
top_stats = snapshot.statistics('lineno')
for stat in top_stats[:10]:
    print(stat)

输出显示某个字典对象一直在增长,占用从几MB涨到了几百MB。

第二步:定位问题代码

追了一下,发现是一个全局缓存字典没有淘汰机制:

python 复制代码
# 问题代码:缓存只进不出
_user_cache = {}

def get_user(user_id):
    if user_id not in _user_cache:
        _user_cache[user_id] = fetch_user_from_db(user_id)
    return _user_cache[user_id]

看起来没问题对吧?但我们的场景是用户量很大,每个用户第一次访问就缓存了,之后永远不会清理。随着用户访问越来越多,缓存就越积越大。

第三步:修复

最简单的方案,换成LRU缓存:

python 复制代码
from functools import lru_cache

@lru_cache(maxsize=10000)
def get_user(user_id):
    return fetch_user_from_db(user_id)

加了maxsize后,缓存超过10000条就自动淘汰最久没访问的。内存占用稳定在300MB左右,问题解决。

反思

其实这个坑很低级,但当时写代码的时候确实没想到用户量会到这个级别。几个教训:

  1. 全局缓存一定要有淘汰策略 ,不管你用lru_cache、cachetools.TTLCache还是Redis,总之不能只进不出
  2. 上线前做一下内存压测,模拟一段时间的持续请求,看内存是否持续增长
  3. tracemalloc是排查Python内存问题的利器,比凭直觉猜靠谱多了

顺便说一句,如果缓存的数据来自API调用,还要考虑缓存过期的问题。之前我有个项目调大模型API,结果缓存了过期的响应,debug了好久才反应过来。国内用这些API有时候响应慢或者不稳定,我后来换了个中转站,响应速度好了不少,缓存命中率也提上去了。

就这些,希望对你有帮助。Python

相关推荐
VidDown7 小时前
24fps 转 25fps 不只是改个数字:帧率转换、PAL/NTSC 与音频补偿
python·网络协议·音视频·视频编解码·视频
死磕音视频9 小时前
让小模型做选择题:限定候选项并读取 logits 概率的入门实践(附 Python 代码)
开发语言·python·大模型·文本分类·transformers
凤山老林9 小时前
Spring Boot 接入国密 SM2/SM3/SM4 的一些实践记录
spring boot·后端·python
pride.li11 小时前
第03章 Python 教程-基础语法:注释与变量
服务器·前端·后端·python·算法·计算机视觉
databook11 小时前
用 Pydantic AI 从零搭建一个能干活的 AI Agent
人工智能·python
康小旺11 小时前
把 markitdown 做成 Windows 桌面版:拖进文档就出 Markdown,附 233MB→64MB 打包裁剪全过程
python
启观川12 小时前
数据结构与算法-第 3 章 常用算法-贪心算法
数据结构·笔记·python·算法·贪心算法
南笙北梦12 小时前
本地开源大模型多轮对话显存泄漏排查与修复实录
python
小羊没烦恼!12 小时前
如何在IIS6,7中部署ASP.NET网站?
数据库·后端·python·web安全·asp.net
钱栈up13 小时前
扫完 3.5 万文件后,我发现了 521 组散落的同名文件
爬虫·python·数据分析