关于内存的那些事
资源紧张的时候,人容易本能地去「省」:关掉不用的服务、调小每个进程的配额、砍掉看似可有可无的功能。
这些都要做,但真正的教训是:「省」只能缓解,不能解决。 你省下来的那点空间,会被某个没预料到的进程吃掉。
这篇讲几轮踩坑之后得到的两条结论——彻底不要比调小更有效,以及资源耗尽时最先倒下的往往不是你在盯的那个。
调小参数不等于解决问题
以数据库为例,缓冲区开得小看似"省内存",但配置背后是一组相互作用的参数:连接数、临时表大小、日志文件尺寸。
把它们各自调小,往往只是把问题从"内存不够"变成"性能不够",或者更糟——从"启动正常"变成"每次重启都要做一次崩溃恢复"。
最终吃掉的还是内存,只是换了个位置和时间点。
一次把账算明白
做界面截图那天,机器上同时跑着这些东西:
| 进程 | 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 没换回来。