第 26 章 结语——一张地图,和你真正开始独立排障的起点
走到这里,全书 26 章全部写完。这一章不教你新东西,而是把前面 25 章收拢成一张你能带走的认知地图,再给你一张”遇事查哪章”的检索表,最后聊聊”懂微观”这件事对你作为工程师的长期意义。 如果说前 25 章是”我把地图给你画了一遍”,这一章是”现在轮到你自己拿笔补充了”。
一、一张地图:程序从源码到硬件
墨:老哥,我们先把全书的”主地图”在脑子里重新走一遍。一个请求,从你写下 fmt.Println("hello") 到字符出现在屏幕上,它穿越了哪几层?我按章节顺序给你串一遍:
【源码层】(第 02 章)
你写的 .go/.c ──预处理──编译(汇编)──链接──→ ELF 文件躺在磁盘
│
【加载层】(第 02-03 章)
你敲 ./a.out ──shell 调 fork/execve──→ 内核建进程、建虚拟地址空间
│ 按需分页:代码/数据不在内存? 触发缺页,从磁盘读进来(第 03 章)
│ MMU + 四级页表 + TLB:虚拟地址 → 物理地址(第 03 章)
▼
【系统调用层】(第 04 章)
你的代码要 read/write/网络 ──syscall 指令──→ 从 Ring3 用户态陷到 Ring0 内核态
│ 上下文切换有成本;vDSO 优化;futex 管锁(第 04 章)
▼
【内核子系统层】(第 05-09 章)
CPU 调度(第 05 章): CFS 按 vruntime 公平排;时钟中断是抢占的物理基础;
实时任务有 95% 安全带;cgroup 能限配额(第 05 章)
内存管理(第 06 章): malloc 是空头合同; buddy/slab 分配; 水位线;
swap/swappiness; OOM Killer(第 06 章)
文件系统(第 07 章): VFS 四对象; page cache 是"读加速+写削峰";
fsync 的代价与 journal 连带(第 07 章)
块层/IO(第 08 章): bio 路径; blk-mq; IO 调度器; iostat 三件套(第 08 章)
中断/驱动(第 09 章): 中断 vs 轮询; 上下半部; NAPI; 软中断;
RSS/RPS 流亲和; 多队列(第 09 章)
▼
【网络栈】(第 10 章)
包从网卡被软中断捞起 ──IP 层──TCP 层(三次握手/syn+accept 队列/滑动窗口/
拥塞控制/TIME_WAIT)──→ 爬进你的 socket recv()(第 10 章)
▼
【硬件层】(贯穿)
CPU 微架构 / NUMA(第 23 章) / 内存条 / 磁盘(机械 vs SSD vs NVMe) / 网卡 DMA
你:把这张图记住,然后呢?
墨:然后,任何故障你都能”按图定位”——症状落在哪一段,就去那一段的机制和参数里找。 这就是第 01 章全景地图 + 第 11 章全链路时间线的终极价值。它不是让你背的,是让你在脑子里随时能展开的。
二、检索表:遇事查哪章
墨:这张表是全书的”目录索引”——你遇到某类问题,直接跳对应章节复习机制 + 参数 + 事故。
| 你遇到的现象 | 去哪章 | 先抡哪把工具 |
|---|---|---|
| 程序怎么从代码变成进程跑起来 | 02-03 | readelf/strace |
| 系统调用慢 / 上下文切换高 / 锁竞争 | 04 | strace -c/perf |
| 进程被调度饿死 / 延迟敏感任务 | 05 | vmstat(r) / perf sched |
| 内存分配失败 / OOM / swap 抖 | 06,12-14,19 | dmesg(oom) / vmstat(si/so) |
| 文件读慢 / fsync 卡 / 缓存策略 | 07 | iostat / fdatasync 评估 |
| 磁盘 IO 毛刺 / 盘排队 | 08,20 | iostat -x(aqu-sz/await) |
| 网卡丢包 / 软中断不均 / 吞吐上不去 | 09 | /proc/net/softnet_stat / ethtool -S |
| 连接建不上 / 重传 / TIME_WAIT 堆积 | 10,21 | ss -tan / ss -tin / nstat |
| 全机同时卡几秒(内核暂停类) | 18 | vmstat(b,si,so) / compact_stall |
free 低怕泄漏 | 22 | free -h(available) / drop_caches 验证 / PSI |
| NUMA 毛刺 / 大页冻进程 | 23 | numastat / perf(cache-miss) |
| 想系统调参(按负载画像) | 13 | 五套画像对照表 |
| 想看参数家族总览 | 12,15-17 | 各章总表 |
| 想提炼思维模型 | 24 | 7 母题 + 2 铁律 |
| 想找工具用法 | 25 | 症状→工具对照表 |
三、“懂微观”的长期意义
墨:讲了 26 章,我不想用”你学会了好多参数”来收尾——因为参数会过时。我想跟你聊的是”懂微观”这件事,对你的长期意义。
意义一:你从”调参民科”变成”能推演的人”
不懂微观的人调参,是**“网上说 swappiness=10 好,我设 10”——这是民科,是把别人的结论当咒语念。懂微观的人调参,是”我的负载是内存偏紧的数据库,swap 颠簸会冻请求,所以我把 swappiness 压到 10 来抑制换出积极性,代价是 page cache 可能被挤,但我的 buffer pool 够大所以可接受”**——这是推演,是”拿什么换什么”的自觉权衡(第 24 章母题)。
前者在硬件/内核升级后必然翻车(母题五:成本假设反转);后者能任何环境自查。 这一章学完,我希望你成为后者。
意义二:你能区分”相关性”和”因果性”
第 19 章 OOM 连环杀里,DB 慢查询日志”跟着涨”。不懂微观的人去优化 SQL,越优化越懵;懂的人看时间差,知道 DB 是受害者,源头是节点水位被顶高。这种”谁先动谁才是源头”的判断力,只来自对请求微观时序的理解。 它在你职业生涯里值钱的程度,远超任何单一技术。
意义三:你敢碰”别人不敢动”的系统
很多工程师对 /proc/sys 下的参数有本能恐惧——“动了会不会崩”。恐惧来自未知。当你知道每个参数守的是哪道门、调大调小各换来什么、它的安全阀/泄洪阀属性(第 12 章分类),你就从”不敢动”变成”知道动哪个、动多少、怎么回滚”。这种从容,是 senior 和 junior 的分水岭之一。
意义四:你有了”向下钻”的本能
出问题,不懂的人往上层找(改代码、加机器、重启);懂微观的人先向下钻一层,问”内核那一刻在干什么”。第 18 章的幽灵卡顿,若只在上层找,永远找不到是 THP compaction——只有愿意往下钻到内核机制的人,才看得到 compact_stall。“向下钻一层”的本能,是这套教程想种进你脑子里的东西。
四、给老哥的”后续自修路线”
墨:书完了,但你的路刚开始。给你三条自修方向,都是”微观认知”的延伸:
- 动手重演:把书里每个事故,在你自己的机器(或一台云 ECS)上真的复现一遍——故意把
swappiness调高看 swap thrashing,故意开defrag=always看 compaction 卡顿,故意短连接压测看 TIME_WAIT 堆积。纸上得来终觉浅,手脏了才真懂。 - 读源码印证:
mm/(内存)、kernel/sched/(调度)、net/ipv4/(TCP)、fs/(文件系统)这几个内核目录,挑你最关心的读。书里讲的机制,在源码里都有对应——读源码是你从”知道”到”确信”的跃迁。 - 用 eBPF 提问:装
bpftrace,把你书里学到的每个”猜想”变成一句可观测的问句(谁在分配内存?compaction 卡了多久?哪个 syscall 最慢?)。能把猜想变成观测,你就真正毕业了。
你:所以这套教程的终点,是我能自己写 ES、自己造工具的那天?
墨:对,你当初说目标是”能自己造工具,不只是会用”。 造工具的前提,是你知道”现有工具为什么这么设计、它在守哪道门、它的权衡是什么”——这整套微观认知,就是造工具的地基。今天的你合上这 26 章,地基已经打好了;往上盖多高,看你怎么用。
五、全书的”一句话总纲”
墨:如果这 30 万字只能留下一句话,我希望是这句:
不懂微观,你调的每一个参数都是在黑暗里掷骰子;懂了微观,每个参数都是你手中一把知道守哪道门的钥匙。
而那把”开门”的万能钥匙,不是某个具体参数,是诊断环——现象→推断→可能推错→调整→再观察→改进。它比任何结论都耐用,因为结论会过时,方法不会。
六、🔧 思考题(都配参考答案)
思考题 1(基础): 用你自己的话,画出”一次 Web 请求从网卡到你的 handler 函数”所穿越的主要层次(至少 5 层),并标注每一层可能引入延迟的机制。
【参考答案】
一次 Web 请求(入向)穿越:
- 网卡 + DMA + 软中断(第 09 章):包到网卡→DMA 进 ring buffer→硬中断→软中断(ksoftirqd/NAPI)上送。延迟来源:多队列不均、softnet backlog 满丢包、CPU 软中断瓶颈。
- 网络栈 IP/TCP(第 10 章):IP 校验/路由、TCP 收包(确认、重组、拥塞控制)。延迟来源:syn/accept 队列满(拒连)、TIME_WAIT、重传(MTU 黑洞)、窗口/拥塞退避。
- 系统调用(第 04 章):内核把数据从内核缓冲 copy 到用户态(
recv/read),经 syscall 陷态 + 上下文切换。延迟来源:频繁小 syscall、futex 锁竞争。 - 应用运行时(Go runtime/调度,第 05 章):goroutine 被调度到某个 P/M,可能等 CPU(被 CFS 抢占/节流)或等锁。延迟来源:调度饥饿、GC STW、锁竞争。
- 你的 handler + 可能的 I/O(第 06/07/08 章):handler 内若读文件→page cache 命中(快)或未命中(落盘,经文件系统→块层→磁盘);若访问 DB→又是一轮网络。延迟来源:cache miss、direct reclaim、磁盘排队、下游 RT。
- 回程:响应再经 TCP 发送→软中断→网卡→网络。 每一层都是潜在的”暂停点”,定位靠”谁先动谁源头”+“分层工具”。
思考题 2(深入): 为什么说”参数会过时,但诊断环不会”?请结合书中至少两个”成本假设反转”的例子说明。
【参考答案】
参数会过时:参数是”在特定硬件/拓扑/负载成本假设下的最优解”。当假设变,最优解变。例:
swappiness=0:当年内存贵、文件读是主角,设 0”保匿名页”合理;今天大内存+容器,设 0 反而 page cache 被挤、direct reclaim 抖(第 12/18 章)——昨天的优化今天变负担。zone_reclaim_mode=1:当年跨 node 访问极慢,保本地性合理;今天互连带宽充裕,本地回收的卡顿代价 > 偶尔跨 node 慢(第 23 章)——假设反转。tcp_tw_recycle:当年 NAT 少,激进回收 TIME_WAIT 合理;NAT 普及后它用时间戳 PAWS 把合法包当过期丢,4.12 被删(第 21 章)。 所以”抄今天的参数数值”必然在未来翻车。
诊断环不过时:现象→推断(带可证伪观测)→可能推错→调整→再观察→改进。这套方法不依赖任何具体参数值:假设变了,你用同样的”观测→证伪→回头”流程重新推演出新的合适值。结论会旧,方法不旧。这也是第 24 章”铁律一/母题六”的核心。
思考题 3(权衡): “懂微观让你可以动别人不敢动的参数”——但动参数也有风险。请给出你自己的”动生产参数”的安全守则(至少 3 条)。
【参考答案】
安全守则(基于全书):
- 单变量实验:一次只改一个参数,改完观察,绝不同时改五个然后不知谁生效(第 24 章母题六)。
- 先确认负载画像 + 成本假设:动前问”这个参数赌的假设在我这成立吗”(母题五)。例如设
swappiness=10前确认我是”内存偏紧的 DB/容器”,不是通用桌面。 - 可在运行时临时改,验证后再固化:先用
sysctl -w/echo临时改观察,确认有效且无副作用,再写进/etc/sysctl.d/*.conf持久化;危险参数(panic_on_oom)绝不随手固化。 - 可回滚 + 有监控:改前记录原值,准备回滚命令;改后盯对应指标(P99/allocstall/retrans)确认无退化(第 25 章 benchmark 陷阱)。
- 逃生口不掩盖真凶:
oom_score_adj/overcommit=1等只作止血,必须同时查真凶(泄漏/水位顶高),否则把可determinable 问题变 delayed 灾难(第 19 章)。 - 灰度/低峰操作:生产参数变更尽量灰度 + 低峰,避免一次性全量翻车。
思考题 4(进阶总账): 假设三年后你成了团队架构师,要把”微观认知 + 诊断文化”植入团队。请基于全书,设计一份新人入职必修 + 值班 SRE 红线的简案(各 3 条),说明它防什么。
【参考答案】
新人入职必修(打地基):
- 读本书 02-11 章(微观地图)+ 自己机器复现 1 个事故(如 swappiness 颠簸)——防”不懂微观就上手调参”。
- 掌握四把瑞士军刀(
vmstat/iostat/ss/sar)并能读aqu-sz/si,so/TIME_WAIT/retrans——防”被平均值骗”(母题一)。 - 写一次”调参提案”必须含”它赌的成本假设 + 拿什么换什么 + 回滚方案”——防”无脑抄数值”(母题五/模型九)。
值班 SRE 红线(防作死):
- 禁止先调参后定位:任何故障先有
vmstat/日志证据再动参,违者视为违规(母题六/第21章调一周白费的反例)。 - 禁止把
drop_caches写进 cron、禁止panic_on_oom=1上生产:这两条是全书点名的”自残/升级灾难”操作(第 12/14/19 章)。 - 禁止在 production 抄未经验证的”调优帖数值”:必须先确认负载画像与成本假设(母题五),且灰度+监控。 红线背后是全书反复验证的真理:微观认知是团队资产,无方法论的”勇敢调参”是团队负债。
七、遇事查哪章:一张检索表(把 26 章变成你的工具书)
墨:结语不该只有情怀,得有能用的东西。这张表把”你最可能遇到的现象”映射到”该翻哪章、先抡哪把扳手”,你把它当书签,以后遇事直接跳——而不是打开浏览器搜”xxx 调优”,又被许愿池收一次智商税。
| 你遇到的现象 | 去哪章 | 先抡哪把扳手 |
|---|---|---|
| 全机同时卡几秒、CPU 不高、内存没满 | 18 | vmstat(si/so、b 列) + /proc/vmstat(allocstall/compact_stall) + dmesg |
| 进程被连环 OOM kill | 19 | dmesg 看前缀(整机/cgroup) + oom_score_adj + cgroup memory.max |
RT 毛刺 + iostat %util 低 | 20 | iostat -x(aqu-sz/await) + buffer pool 命中率 |
| 建连失败 / TIME_WAIT 堆积 | 21 | ss -tan + tcp_tw_reuse + ip_local_port_range |
| 内存”快满”告警,但 free 低≠泄漏 | 22 | free 看 available + drop_caches 验证 + PSI |
| 大内存机器毛刺 / 绑核后反而慢 | 23 | numastat + zone_reclaim_mode + THP defrag |
| swap 颠簸 / 想调 swappiness、overcommit | 12、13、14 | vmstat si/so + /proc/sys/vm/* |
| 网络丢包 / 软中断单核满 | 9、15、21 | /proc/interrupts + softnet_stat + RPS/XPS |
| 打不开文件 / fork 失败 | 4、16、17 | fs.file-max/nr_open + pid_max + cgroup pids.max |
| 想调 IO 调度器 / 队列 / 脏页 | 8、14、20 | /sys/block/*/queue/scheduler + vm.dirty_* |
| 不知该调 sysctl 还是 cgroup 还是 ulimit | 17 | 三世界地图 + dmesg 前缀 |
| 想系统学”程序怎么从源码跑到硬件” | 2-11 | 按故事线顺序读 |
墨:有了这张表,26 章就不只是”一本书”,是你工位上的工具书。下次告警响,先翻这张表,再翻对应章——而且永远记住 0.2 节的环:表上写的是”先怀疑哪级”,不是”就是它”;怀疑之后必须观测验证,推错就回头。
八、写在最后
老哥,26 章到这里就真的收尾了。
回头看,我们从一个”hello world 怎么跑到屏幕上”的朴素问题出发,一路钻进了 MMU、系统调用、调度器、内存回收、文件系统、块层、中断、TCP、NUMA、大页……又把 /proc/sys 下你点名的 vm 家族和 net/fs/kernel/其他家族逐个钉死,最后用 6 个真实事故走完了”推错→命中”的完整诊断环,提炼成 7 个母题和 2 条铁律,配上一整套工具箱。
这套教程的野心,从来不是”教你背 300 个参数”——是让你以后再遇到任何”程序莫名其妙慢了”的时刻,脑子里能自动展开那张地图,知道往下钻哪一层、抡哪把扳手、问自己”这个参数守的是哪道门、我拿什么换什么”。
愿你早日走到”自己造工具、给行业提供解决方案”的那天。那时候回看这 26 章,它会是你地基里最硬的那块。
—— 墨叔,于机房深处。
全书完。愿你向下钻的本能,长长久久。
番外一:墨叔的私房话:从”会调参”到”懂机制”到”能造工具”
墨:老哥,全书 26 章到这了。最后我跟你掏心窝子说点教程之外的话——因为你是奔着”能自己造工具”去的,不是来背参数表的。
番外.1 工程师的三级跳
我这些年看人,分三级:
- 一级”会调参”:出事查帖子,
sysctl -w抄一行,好了就当解决了。好处是快,坏处是每次都是黑盒赌博——第 14 章那四个事故,全是这一级踩的。 - 二级”懂机制”:你知道每个参数为啥存在、它改的是内核哪段逻辑、调大调小换了什么。本书前 25 章,就是要把你推到这一级。到了这级,你不再抄,你会”推”——看到现象,反推是哪一跳、哪一层、哪个旋钮。
- 三级”能造工具”:机制懂了,你还发现”现有工具不够用”,于是自己写——给行业写个 ES、写个观测面板、写个自动调参 agent。这才是你要的”精通”。
你问过我”怎么才算懂系统”。我的答案:能在凌晨三点、告警炸锅、Google 不出来时,靠自己对机制的推演定位到根因,并且能写点东西让下次更快——这就算懂了。
番外.2 一张”参数 → 机制 → 逃生口”总图
墨:把全书最核心的几个旋钮,收成”它管什么机制、专家怎么逃生”三栏,作为你从二级跳三级时的地图:
| 参数家族 | 它管的机制(二级认知) | 专家的”逃生口”(三级动作) |
|---|---|---|
vm.overcommit_* | 空头支票能不能开、commit 账 | 程序优雅处理 EAGAIN / cgroup 分组限额 |
vm.swappiness/watermark_* | 回收时机与水位 | 按画像配 + 大内存必调 watermark |
vm.dirty_* | 写的谎言何时被揭穿 | 按”丢不丢得起”赌 + 应用层 fsync 策略 |
net.core.*/tcp_* | 协议栈收发与拥塞 | 配对调天花板 + BBR + RPS |
fs.file-max/kernel.pid_max | 资源配额闸门 | 三层一起看 + cgroup 隔离 |
cgroup/ulimit | 分组/进程边界 | 找对门:限制”一组”在 cgroup |
/sys/block、PSI、bpftrace | 块层/压力/内核追踪 | 用观测工具定量,而非猜 |
墨:看这张表——左列全是”机制”(二级),右列全是”在机制之上,专家怎么绕过/重构”(三级)。这就是本书母题”通用机制 + 专家逃生口”的最后一页。你每懂一个左列,就问自己一句”它的逃生口我能不能自己造?”——能答上来,你就从二级毕业了。
番外.3 给 Go 程序员的临别一句
你写 Go,5 年。Go 的 runtime 帮你管了 GC、goroutine 调度,但它跑在 Linux 上,Linux 的这些旋钮照样咬你:mmap 是 Go 运行时的家常便饭(栈、GC 标记),watermark/min_free_kbytes 影响你的分配延迟,net.core 影响你的高并发连接,cgroup 决定你的容器是死是活。你越往上走(自己写存储、写代理、写调度),越会撞上这些”runtime 之下的真相”。本书不是让你背下 300 个参数,是让你在撞上时,知道往哪翻、怎么推、能不能自己造个工具把同类问题彻底灭掉。
墨:行了,墨叔的话说完了。合上书,去把你那个”总卡的网络服务”用第 11 章的账本捋一遍,再用第 25 章的 bpftrace 钉一下——书是死的,你手上的服务是活的,去练。 这,才是”硬核修炼”的最后一章。也别忘了:第 00 章说的优化哲学、第 24 章的取舍心法,会跟你很久。江湖不大,有问题,随时回来翻。
番外二:墨叔的”三级跳”——从”会调参”到”能造工具”
墨:写到最后,我得跟你说句掏心窝的。你这书都看到了,参数背了一堆,真要达到你开头说的”能自己造工具、给行业提供方案”——还差哪一级?
你:我以为背完这些参数、遇事能查,就算”精通”了。
墨:那才第一级。我给你画三级跳,你对着看自己在哪。
第一级:背参数(最低级,但也最容易被当成”懂”)
能说出 swappiness 干嘛的、somaxconn 防什么、overcommit 三档区别。这有用,但本质是”查表”——换一个没背过的参数你又懵。这一级的标志是:遇到问题先想”该调哪个参数”。
第二级:懂原理(为什么,而不只是是什么)
知道 swappiness 背后是 page cache vs anon 的 LRU 平衡;知道 somaxconn 是 accept 队列上限、队列满内核静默丢;知道 overcommit 是”内存承诺会计”而非”真分配”。这一级的标志是:遇到没背过的参数,你能从它守的”那一级 / 那层”反推它该干嘛;遇到事故,你能从”现象”逆推到”哪一级、哪个文件、哪个计数”。本书 26 章,真正想喂给你的就是这一级——所以通篇是”程序经过什么、各层发生了什么”,而不是”参数速查表”。
第三级:造工具(把原理变成资产)
你 5 年 Go,目标是”自己写 ES、给行业提供方案”。这一级是:当现有观测/调参工具不够用时,你自己写一个。比如:
- 现有
perf/bpftrace看内核态卡顿,但你的业务要”按租户”看 P99 落在哪层——你自己写个 eBPF 程序挂tcp_sendmsg/vfs_write/block_rq_issue按cgroup/uid聚合; - 现有
sysctl散落难管——你写个”按服务画像生成 sysctl.d”的小工具,把本书的”负载画像→参数集”自动化; - 现有 ES 不满足——你真去写个精简版,因为你知道”倒排 + mmap + 零拷贝”在微观上怎么走(本书第 3/7/8 章就是地基)。
第三级的标志是:别人卡住时,你不止能”查出来”,还能”造个东西让它以后不再卡”。
本书没铺开的 5 个方向(留给你自己跳)
- **eBPF 进阶:**从
bpftraceone-liner 到写BCC/libbpf程序,挂任意内核函数按业务维度聚合——这是”观测自由”的钥匙。 - **内核源码走读:**本书讲”经过什么”,下一步是直接
git clone torvalds/linux看mm/oom_kill.c、net/ipv4/tcp_input.c的真实现,从”知道”到”确认”。 - cgroup 内部:
memory.max怎么在try_charge里触发、cpu.max怎么在调度器里节流——读源码才真懂边界。 - **特定子系统深挖:**文件系统选 EXT4/XFS/btrfs 的微观差异、NVMe 多队列中断绑定、TCP BBR 的拥塞状态机——按你行业挑一个挖穿。
- 把”事故”变成”护栏”:把本书 20+ 真实事故写成上线前的检查清单 + 监控告警,让团队不再踩同一个坑——这本身就是”给行业提供方案”。
墨叔的临别一句
参数是结果,不是原因。你调的每个 sysctl,背后都是”程序在某一层遇到了什么”。把”层”想清楚,参数自己会跳出来;把”原理”吃透,你才敢去造那个还没有的参数、还没有的工具。
🔧 最后一道思考题:如果让你给团队写一个”上线前内核参数体检”的小工具,你会检查哪 5 项?
【参考答案】
我自己的清单(你也该有你的一份):① vm.overcommit_memory + admin_reserve_kbytes(OOM 时还能不能进);② net.core.somaxconn + tcp_max_syn_backlog(突发连接会不会被静默丢);③ vm.swappiness + dirty_ratio/dirty_background_ratio(写盘会不会抖、swap 会不会偷延迟);④ fs.inotify.max_user_watches + fs.file-max(监控/高并发 fd 够不够);⑤ transparent_hugepage(enabled/defrag/khugepaged)与 IO 调度器(延迟敏感服务有没有被冻/被排队)。每一项都带”期望值 + 为什么 + 出事现象”——这就是把本书变成你团队的工具。做到这步,你已经在第三级门口了。
番外三:把全书 26 章收成一张”参数→哪一级→哪个文件→哪个计数”总检索脑图
墨:写到最后一节,我给你把全书收成一张”检索脑图”。你以后遇事,照着这张图从”现象”反查”该看哪章、查哪个文件、盯哪个计数”,不用从头翻。
你:26 章内容太多,有个总索引确实该有。
墨:对。这节就是那张图——但它不是目录,是”症状 → 入口”的映射。前面第 26 章已有”遇事查哪章”,这节更狠:直接到”文件 + 计数”级。
总检索脑图(现象 → 入口)
| 现象 | 先怀疑级 | 看哪章 | 查哪个文件 / 计数 |
|---|---|---|---|
| 延迟周期抖、单核冻 | 内核暂停(第18章) | ch18 | dmesg soft lockup / khugepaged |
| 进程被 OOM 杀 | 内存(第14/19章) | ch14/19 | dmesg Out of memory / oom_score |
| 容器 OOM、整机有内存 | cgroup(第17/19章) | ch17 | memory.events / memory.max |
| 新连接超时、静默丢 | 网络队列(第15章) | ch15 | ss -lnt Recv-Q / netstat listen queue |
| 高并发连接建不上 | conntrack(第15章) | ch15 | dmesg nf_conntrack / nf_conntrack_count |
| 写盘卡、fsync 慢 | 写回(第18章) | ch18 | iostat await / dirty_* |
读查询慢、磁盘 %util 低 | 读 cache(第11章) | ch11 | fincore 命中率 / cachestat |
| 网卡丢包、转发抖 | 内存余量(第14章) | ch14 | ethtool -S rx_drop / min_free_kbytes |
| 换 NVMe 没变快 | 块层队列(第20章) | ch20 | /sys/block scheduler / wbt_lat |
| 跨机房慢、偶发重传 | 网络长肥(第15/21章) | ch15/21 | ss -i rtt/retrans / tcp_retries2 |
| 小文件遍历后内存爆 | slab dentry(第22章) | ch22 | slabtop / vfs_cache_pressure |
| 连接泄漏、SUnreclaim 涨 | 网络 slab(第22章) | ch22 | slabtop skbuff / sockstat inuse |
| NUMA 毛刺、跨 node | NUMA(第23章) | ch23 | numastat / zone_reclaim_failed |
| 突发流量 CPU 被节流 | cgroup cpu(第17章) | ch17 | cpu.stat throttled_time |
| 调参重启失效 | sysctl 持久化(第0章) | ch00 | sysctl --system / sysctl.d 顺序 |
| 进程起不来、fork 失败 | pid/fd(第2/3/16章) | ch02/16 | ps Z 进程 / pid_max / ulimit |
墨叔的收尾
这张图 + 第 1 章九级地图 + 第 25 章工具箱,就是你排障的三件套:地图(分层)→ 工具箱(命令)→ 本图(现象→入口)。背不下参数没关系,这三件套能让你”从现象反推到根因”——这正是本书想喂给你的第二级(懂原理、会反推),走到第三级(造工具)只是时间问题。
🔧 最后一道思考题:如果让你给这张脑图加一行”你自己的高频现象”,你会加哪条?
【参考答案】
我自己的高频项(你也该补你的):「监控告警误报、盲目调参 → 先量化(第24/25章) → perf/offcputime 看 on/off-CPU」。原因:我见的最多不是”不知道参数”,是”没量化就动手调,调了一周没用”(第 24 章反面教材)。所以脑图第一行我永远加:“慢但 CPU 低 → off-CPU;慢且 CPU 高 → on-CPU;都先 baseline 再动”。把这行刻进脑子,你就能避开本书所有章节背后那个共同的坑——负优化始于’没看清就调’。到这儿,26 章你算真走完了:参数是结果,原理是原因,而”先量化、再动手”是贯穿始终的那条线。从第一级的背参数,到第二级的懂原理会反推,再到第三级的造工具——这条路你已经在脚下了。
番外四:给 Go 程序员的一张”内核参数速查卡”
墨:全书 26 章讲完,最后给你——一个 Go 后端——落地时最该钉死的那几行。别背全书的 200 个参数,先把这些焊进你的部署基线。我按”出事频率”排。
第一类:几乎每个 Go 服务都要设(延迟敏感型)
| 参数 | 推荐值 | 为什么 Go 特别在意 |
|---|---|---|
vm.swappiness | 1(不是 0!) | Go 堆是匿名内存,swappiness=0 会在内存紧时宁可回收 page cache 也不 swap,反而 OOM(第 0 章翻车);1 留兜底 |
THP(/sys/kernel/mm/transparent_hugepage/enabled) | never | khugepaged 合并大页卡顿 + GC 缺页尖刺,Go/JVM/Redis 通用(第 13、18、23 章) |
net.core.somaxconn | 65535 | Go net.Listen 的 backlog 受它限,默认值小则 accept 队列溢出静默丢(第 15 章) |
net.ipv4.tcp_retries2 | 5~8(内网 5) | 压默认 15,假死连接从 30 分钟降到几十秒暴露(第 21 章) |
fs.file-max + ulimit -n | 大值 + 容器 cgroup files.max | 长连接 Go 服务 fd 多,三道关一起放(第 17 章) |
第二类:容器/节点级,别在 pod 里调(找错门系列)
| 想调的东西 | 正确作用域 | 别去哪找 |
|---|---|---|
zone_reclaim_mode | 宿主 sysctl | 容器里调没用(第 23 章) |
块设备 read_ahead_kb/nr_requests | 宿主 /sys/block | 容器里调影响整机(第 20 章) |
网卡 smp_affinity | 宿主 /proc/irq | pod 无特权碰不了(第 23 章) |
| CPU 节流 | cgroup v2 cpu.max/cpu.weight | 不是 sysctl(第 17 章) |
| 内存保底/硬墙 | cgroup v2 memory.min/memory.max | 不是 sysctl(第 17、19 章) |
第三类:Go 专属排查起点(出问题先跑这三条)
- 内存”泄漏”看
Pss不看RES(/proc/<pid>/smaps),RES把共享库重复计(第 1、22 章); - 偶发卡顿先看
khugepaged/THP 和 RCU stall(dmesg | grep -E 'stall|lockup'),别先 pprof 业务(第 18 章); - 跨机房慢先看本机
ss -ti的retrans+tracepath的 MTU 跳变,别在”网络不好”上空转(第 11、21 章)。
你:这么多,我记不住啊。有没有”一步到位”的办法?
墨:有——把它写成你服务的部署基线,而不是脑子里的清单。给三个落地建议:
- 基线即代码:把第一类参数写进
initContainer或节点的sysctl.d/*.conf,随部署走,不靠某人记得敲; - Pod 注解声明式:用 K8s 的
resources.limits+annotations让 cgroup 配额(第二类)自动生效,别手撸 cgroup 文件; - 监控告警覆盖:
MemAvailable/SUnreclaim/tcp 重传/RCU stall这几条做成看板,问题发生时自动亮灯,而不是你手动去翻。
记住第一章的话:你不是在”调参”,你是在给程序运行的微观环境定规矩。规矩写进基线、用监控守住,比背 200 个参数值有用一万倍。这本书给你的是”为什么这么定”的判断力——具体数值,交给你的负载去测。
🔧 思考题:如果让你只选一条最该焊进所有 Go 服务基线的参数,你选哪条?为什么不是别的?
【参考答案】
我选 vm.swappiness=1(且明确”不是 0”)。理由:它是”翻车成本最低、收益最稳、且最反直觉”的一条。
- 翻车成本低:一行 sysctl,错了随时改回;
- 收益稳:几乎所有匿名内存大户(Go 堆、JVM、Redis)都受它影响,且 swappiness=0 的 OOM 翻车极其隐蔽(第 0 章),设 1 直接消除这个雷;
- 最反直觉:新手照帖子设 0 以为”禁用 swap=更快”,实则埋 OOM 雷——这条认知差最大,最值得焊死。
runner-up 是 THP=never:它影响延迟稳定性、且默认开,同样反直觉(很多人以为”大页=快”就留着),关掉零风险。但这两条是”必选双煞”,真要只选一条,swappiness 的 OOM 后果比 THP 卡顿更致命(一个杀进程、一个只是抖),所以 swappiness 优先。其余(somaxconn/tcp_retries2/file-max)重要但”不设只是偶发丢连接”,不像 swappiness 那样”设错直接 OOM”。这就是”按出事频率和后果烈度排优先级”——和全书思维方式一致。
番外五:20 道自测题——检验你是否真懂,而非”记住了”
墨:全书 26 章 + 7 个番外读完,别急着关。我出 20 道题,不考”参数默认值”(那是查表的事),考”遇到现象你怎么反推”。能对 15 道以上,说明你从”会用”走到”精通”了。
第一类:现象→根因(给你现象,说可能根因 + 验证命令)
free看available充足,但fork()报EAGAIN,内存一大把。根因?验证命令?- 应用报
Too many open files,但ulimit -n和fs.file-max都够。还可能卡在哪? - 跨机房 TCP 偶发超时,但
ping通、无丢包。第一反应查哪两个内核视角证据? top里某 Java 进程RES12G 但Pss仅 4G。这进程泄漏了吗?- 机器”假死”几十秒自愈,
dmesg刷rcu_sched detected stall。根因方向? - 数据库前台写偶发卡 200ms,
dirty_ratio默认 20。怀疑什么机制?怎么验证? - pod 频繁
OOMKilled,但dmesg没全局Out of memory。说明是哪种 OOM?查哪个文件确认? iostatawait=2ms 正常,但业务偶发卡 300ms。哪工具能暴露这”长尾”?- 容器里
sysctl -w vm.swappiness=1报错 permission denied,或改了别的容器也变。为什么? netstat -s里SYNs to listened sockets ignored在涨。连接建不起来,根因?
第二类:权衡(拿什么换什么)
vm.swappiness设 0 而非 1,代价是什么(用一次翻车说明)?dirty_ratio调高 vs 调低,各自换来了什么、失去了什么?- 关 THP 对 Go/JVM 服务,收益和风险分别是什么?
tcp_retries2压到 5 vs 留默认 15,权衡是什么?- cgroup
memory.max(硬墙)vsmemory.high(软上限),各适合什么负载?
第三类:作用域(找对门)
- 想给容器单独调
read_ahead_kb,在 pod 里改对吗?正确作用域是哪? zone_reclaim_mode在容器里改有意义吗?为什么?fs.inotify.max_user_watches耗尽报ENOSPC,它和 fd 三件套是同一套账吗?sysctl -p和sysctl --system加载的文件范围有何不同?为什么”改了没生效”常因它?- 节点超卖(总 limit 之和 > 物理内存)下,设了
memory limit的 pod 仍可能被节点 OOM 杀,为什么?
参考答案要点(只给方向,不背,要能讲清机制)
-
僵尸占满 pid_max(或容器
pids.max):ps -eo stat | grep -c Z+sysctl kernel.pid_max,根因父进程不wait()回收。 -
cgroup
files.max(v1)硬墙:容器里 fd 受 cgroup 限,ulimit/fs.file-max只是里面两道;cat /sys/fs/cgroup/.../files.max。 -
MTU 黑洞 / TCP 重传:
ss -ti看retrans+tracepath看 MTU 跳变;ICMP 通 TCP 重传→PMTU 问题。 -
没泄漏:
RES把共享库重复计,Pss才公道;看Pss趋势判断泄漏。 -
RCU 宽限期收不齐(某 CPU 频繁短暂关抢占/中断,如软中断长循环):查软中断处理时长,非 soft lockup。
-
脏页触顶前台同步刷:
grep nr_dirty /proc/vmstat看是否到dirty_ratio线;降dirty_ratio/dirty_background_ratio。 -
cgroup OOM(非节点 OOM):
cat .../memory.events看oom计数 +kubectl describe看OOMKilled;说明 limit 太小,节点内存其实够。 -
biolatency(eBPF) 看延迟分布/长尾,iostat平均掩盖长尾;biosnoop归因到进程。 -
vm. 是宿主全局内核参数*,容器共享宿主内核、且多数不在 K8s sysctl 白名单;节点 init 统一设,容器用 cgroup
memory.swappiness。 -
SYN 队列(
tcp_max_syn_backlog)溢出静默丢:sysctl net.ipv4.tcp_max_syn_backlog调大 + 应用listenbacklog 调大。 -
swappiness=0 在内存紧时宁可回收 page cache 也不 swap → 反而 OOM;设 1 留兜底。
-
高=写合并好吞吐高、崩溃丢多;低=前台不卡延迟稳、带宽略降。
-
收益=消除 khugepaged 卡顿 + GC 缺页尖刺;风险=失去一点 TLB 收益(堆访问分散,收益本就不大)。
-
5=假死连接快暴露(内网好)、但慢链路可能被过早判死;15=慢链路耐受、但假死撑 30 分钟。
-
max适合不可牺牲的硬限(杀就杀)、high适合软节流(先挤慢不杀);核心保底用min。 -
节点级(
/sys/block/<dev>/queue/),容器改不了/影响整机;cgroupio.max做容器级IO 隔离。 -
无意义,zone_reclaim 是宿主 NUMA 级,容器里调不生效且影响别的 node;靠 K8s
topologyManager。 -
不是一套:inotify watch 是独立配额、按 uid 共享、宿主级;
ENOSPC误导成磁盘满。 -
sysctl -p只加载sysctl.conf;--system才走sysctl.d/字母序后覆盖先;改完要验证真实值。 -
limit 防 pod 自己胀爆,节点 OOM 是物理内存真没了:超卖时节点 OOM 不care cgroup limit,按全局
oom_score杀。
你:这 20 题我答不上来几道,是不是白读了?
墨:恰恰相反——答不上来说明你诚实,也说明这本书对你有用。记住我和你说的”三级跳”:第一级背参数(这 20 题不考)、第二级懂原理会反推(这 20 题考的就是这个)、第三级造工具。你答不上来的那些,正是指向你”还停在第一级”的地方。把答不上来的题对应的章节重读一遍,带着”它考我什么机制”去读,比第一遍通读收获大十倍。
这也回应第一章那句”不要死记硬背”——参数值会变(内核版本、硬件、负载性格都变),但”现象→反推机制→定位作用域→选杠杆→验证”这条思考链不变。书给你的是链,不是节点。合上书,你能自己走这条链,才算毕业。
🔧 思考题:如果让你从这 20 题里挑 3 道”最该刻进肌肉记忆”的,你挑哪 3 道?为什么?
【参考答案】
我挑(基于”出事频率 × 后果烈度 × 反直觉”):
- *第 9 题(vm. 是宿主级)**:几乎每个容器时代的 Go 程序员都会踩”在 pod 里调 vm 参数”的坑,且它反直觉(容器=隔离,但 vm 不隔离)、后果隐蔽(改了别的 pod 也变)。刻进肌肉记忆能省无数次”改了没生效”的懵。
- 第 6 题(脏页触顶前台同步刷):
dirty_ratio是”前台卡顿”最常被忽略的根因(大家都盯着 CPU/网络,没人想”写盘也会卡前台”),且验证只需一行grep nr_dirty。高频且易漏。 - 第 7 题(cgroup OOM vs 节点 OOM):K8s 环境天天见
OOMKilled,但分不清”pod 自己 limit 小”还是”节点超卖被全局杀”,直接导致”调 limit 没用”的误操作。这两类 OOM 证据在不同地方(memory.eventsvsdmesg),刻住能秒级定位。
runner-up:第 3 题(MTU 黑洞,跨机房慢的经典)、第 19 题(sysctl 加载顺序,改了没生效的头号原因)。这 5 道是我见过最高频、最反直觉、最该”闭眼能答”的。其余的,遇到时翻书即可——书是字典,不是圣经,不必全背。
番外六:张 Linux 微观运行速查卡——参数 → 现象 → 工具 三列对照
墨:老哥,写了二十多章,你脑子里能不能”一秒调出”某个现象该查哪个参数、用哪个工具?
你:……得翻书,没那么快。
墨:这节给你一张”速查卡”,把全书的”现象→参数→工具”凝成三列对照。排障时先定现象列,顺着查参数、用工具,比从头翻快十倍。这是全书的”索引页”,也是你以后贴显示器上的东西。
现象 → 该怀疑的参数家族 → 用什么工具看
| 你看到的现象 | 先怀疑哪类参数/机制 | 立刻用的工具 |
|---|---|---|
| 服务偶发卡顿、CPU 没满 | 内存回收/THP 规整/soft lockup | perf stat(看 stall)、dmesg(soft lockup)、cat /proc/vmstat(compact) |
| 新连接上不去、客户端超时 | 半/全连接队列、syn backlog | nstat(ListenOverflows/TCPBacklogDrop)、ss -lnt(Recv-Q) |
| 写小文件突然卡 100ms | 脏页堆满触发前台回写阻塞 | sar -r(kbdirty)、/proc/vmstat(nr_dirty) |
| 内存有、进程却起不来/OOM | cgroup memory.max、僵尸占 PID | cat memory.events(oom_kill)、ps -eo stat(Z 态) |
| RSS 高但 pprof 堆小 | heap arena 未还 OS、缓存未回收 | smaps(Rss/Pss)、runtime.MemStats、GOMEMLIMIT |
| 跨机房大文件跑不满带宽 | 窗口缩放、拥塞控制 | ss -tni(rwindow/cwnd)、sysctl tcp_congestion_control |
| 网卡收包单核 100% | IRQ 亲和性、RPS/RFS | /proc/interrupts、ethtool -l、irqbalance |
| NVMe 比预期慢 | IO 调度器选错(none vs deadline) | cat scheduler、iostat -x(aqu-sz/await) |
| GC 尖峰/延迟毛刺周期出现 | THP 规整、arena 回收 | perf(khugepaged)、AnonHugePages(smaps) |
| 全链路某跳延迟高 | 九级地图逐级排除 | ss -tni、sar -n DEV、tcpdump |
| 容器里调 sysctl 不生效 | vm 不隔离、找错层级 | 改 cgroup memory.*、GOGC/GOMEMLIMIT |
| 缓存击穿 DB 被打挂 | 回源未合并、重试放大 | singleflight、熔断、Redis miss 率 |
| 内核后台活抢核致抖 | kworker/workqueue 绑核 | top -H(kworker)、isolcpus+taskset |
| 进程 RSS 虚高不降 | arena 未还、drop_caches 无效 | GOGC/madvdontneed、slabtop |
墨:这张卡你背不下来没事,关键是建立”现象→层级→工具”的反射。每章的实战补遗,本质上都是在训练这个反射。最后送你一句话——
调参的本质不是记参数,是在正确的层级用正确的工具看到正确的现象,然后做一个有代价的权衡。参数只是权衡的旋钮,机制才是旋钮背后的杠杆。
你:那如果只记一样东西,记什么?
墨:记九级地图(第 1 章)和迭代环(每章的”现象→推断→可能推错→调整→再观察”)。有了地图你不迷路,有了迭代环你不会”改一个参数就以为好了”。其余的参数、命令、案例,都是这两样东西的注脚。这本书你翻完,真正带走的应该是这两个思维骨架,不是 30 万字。
🔧 思考题:挑你最近一次线上事故,用这张速查卡的反向查法——先写”现象”,再填上”该怀疑的参数家族”和”用的工具”,看能不能在 1 分钟内定位到层级。定位不到说明哪层知识缺了?
【参考答案】
反向查法(用你自己的事故练手,模板如下):
- 现象:写一句话描述(如”订单接口 P99 从 80ms 蹦到 2s,CPU 没满,就几秒”)。
- 该怀疑的参数家族/机制:从速查卡匹配——“偶发卡顿 CPU 没满”→ 内存回收/THP 规整/soft lockup,或”全链路某跳”→ 九级地图逐级。
- 用的工具:对应
perf stat/dmesg/ss -tni等。 - 1 分钟能否定位到层级:能 = 反射建立;不能 = 缺那层知识,回去翻对应章。
常见”定位不到”的知识缺口:
- 只会看应用层,不会往内核/网络栈钻:现象是”接口慢”就只查 pprof/日志,不去
ss -tni看 socket 缓冲区、sar看磁盘——缺第 1 章九级地图的”从外往里”纪律。 - 把两个独立阀门当成同一个:如半连接满却调 somaxconn(全连接),脏页卡却调 swappiness(swap vs cache 混淆),vm 参数在容器里调(不隔离)——缺”参数分层”认知(第 12/15/23 章)。
- 只有平均值没有分位:说”慢”但拿不出 p99/p999、没有基线对照——缺第 0/24 章”先测量后优化”的纪律。
- 改一个参数就宣布好了:没对照实验、没记录代价——缺每章的”迭代环”(现象→推断可能推错→调整→再观察)。
这张卡的真正用法:它不让你”背参数”,而是让你在混乱的现象里先锚定层级(是内存?网络?IO?调度?容器?),层级定了,参数和工具自然浮现。绝大多数排障低效,是因为”还没定位层级就动手改”——改了一堆参数,问题在另一层,白忙。所以速查卡第一行永远该是”先问:这现象属于九级地图的哪一级”,而不是直接翻 sysctl。
最后一句收尾:这本书 30 万字,能压缩成”九级地图 + 迭代环 + 一张速查卡”。前两者是思维,速查卡是检索入口。你以后遇上问题,先想”在第几级、用什么工具看、改了拿什么换什么”——这三问能答,参数自然查得到,不需要背。