一次 OOM 排查记录

上周三凌晨 3 点，Prometheus 告警炸了：一个 Python worker 进程占了 8GB 内存，整个服务被 OOM Killer 干掉，重启后 4 小时又挂了。看数据打开 dmesg | grep -i oom： [Wed Jun 18 03:14:22 2025] python3 invoked oom-killer: gfp_mask=0x100cca(GFP_HIGHUSER_MOVABLE), order=0 [Wed Jun 18 03:14:22 2025] Memory cgroup out of memory: Killed process 18472 (python3) total-vm:8354320kB 挂 tracemalloc 给进程加个 tracemalloc，重启，等 4 小时再炸。看 top 20 分配点： 5.2 GB /usr/lib/python3/dist-packages/pandas/core/frame.py:5341 2.1 GB /usr/local/lib/python3.11/dist-packages/our_app/etl.py:87 1.0 GB /usr/local/lib/python3.11/dist-packages/our_app/cache.py:23 问题在 etl.py:87，我自己的代码。代码是这样的 def merge_old_new(old_df, new_df): # 我以为是流式合并 result = pd.concat([old_df, new_df], ignore_index=True) result = result.drop_duplicates(subset=['id']) return result old_df 是从数据库全量加载的，8 亿行。每次调用，pd.concat 都会复制一份，然后 drop_duplicates 又复制一份。怎么修 def merge_old_new(old_ids: set, new_df): # old_ids 是 id 集合，不是全量 DataFrame mask = ~new_df['id'].isin(old_ids) return new_df[mask] 复盘没有 "内存使用趋势" 看板，没看到缓慢上涨没给 DataFrame 内存使用加 metric 写代码时没想数据增长，这是个常见错误留个备忘 Python 处理数据，三件套要警觉： pd.concat：永远 O(n+m)，大表不要 concat df.drop_duplicates()：会复制内存，能 SQL 做就 SQL 做列表推导 + 字符串拼接：用 str.join，不要用 +