◈ 夜宵吧

关于内存的那些事

资源紧张的时候,人容易本能地去「省」:关掉不用的服务、调小每个进程的配额、砍掉看似可有可无的功能。

这些都要做,但真正的教训是:「省」只能缓解,不能解决。 你省下来的那点空间,会被某个没预料到的进程吃掉。

这篇讲几轮踩坑之后得到的两条结论——彻底不要比调小更有效,以及资源耗尽时最先倒下的往往不是你在盯的那个。

调小参数不等于解决问题

以数据库为例,缓冲区开得小看似"省内存",但配置背后是一组相互作用的参数:连接数、临时表大小、日志文件尺寸。

把它们各自调小,往往只是把问题从"内存不够"变成"性能不够",或者更糟——从"启动正常"变成"每次重启都要做一次崩溃恢复"。

最终吃掉的还是内存,只是换了个位置和时间点。

一次把账算明白

做界面截图那天,机器上同时跑着这些东西:

进程 RSS
chromium × 4 537 MB
node(这个站) 83 MB
v2ray 54 MB
python(另一个面板) 44 MB

内存总共 1031 MB。

站点进程占其中的 11%。而那几天我在做的事是:把首页 HTML 从 10 KB 压到 2.9 KB,把配图从 300 KB 压到 90 KB。

一个后台开着的浏览器,是这个站点的六倍半。

这不是说优化没用,而是说优化的量级要对得上。省 200 字节和少开一个 tab,在同一个预算里不构成同一个数量级的决策。

那天之后我的做法变了:先看 free -m 的总水位,再去认领「哪个进程在吃」。如果只看自己那一项,很容易得出「我优化得很成功」的结论,同时机器在 swap。

彻底不要,比调小更有效

更可靠的做法是规划:先盘点每一项占用,想清楚哪几项是必须的、哪几项可以彻底不要。

彻底不要,比调小更有效。 一个关掉的服务不占内存,也不会在你没注意的时候被重新拉起;一个调小了配额的服务,仍然在那里等着某天被流量压垮。

最先出问题的不是你最关心的那个

还有一个容易忽略的点:资源耗尽时,最先出问题的往往不是你最关心的那个服务。

被牺牲的通常是占用最大、或者运气最差的那个。你盯着主服务,挂的是旁边的附属进程;等它挂完,主服务才因为依赖缺失跟着倒。

所以光监控主服务是不够的,要看整体水位。

薄的技术栈好算账

资源不是省出来的,是规划出来的。

这也是为什么后来我倾向于把技术栈做薄。薄的东西,好算账——你知道每一层在做什么,也知道出问题时该去哪里找。

厚的技术栈不是不能跑,只是当资源真的紧张时,你会发现自己要先花半天时间搞清楚"到底是谁在吃内存"。

两层账,都要看

free -m 里的 used 和 ps 里各进程 RSS 加起来对不上,这是正常的:page cache 算在 used 里但不属于任何进程,共享页会被多个进程重复计数。

所以两本账各管一件事:

  • free -m 的 available:这台机器现在还能分配多少。它决定「会不会出事」。
  • cgroup 的 memory.current:这个服务自己用了多少。它决定「是谁在用」。

这台机器现在的两个数:available 636 MB,站点 cgroup current 36 MB、peak 60 MB。

只看前者会不知道谁在吃;只看后者会误以为机器很空——那时候 swap 里还压着 198 MB 没换回来。