practical - casebook

Practical Casebook:学完本章到底能解决什么问题

这份 casebook 用真实问题把四大件串起来。它不是题库,而是训练一种能力:

看到现象
-> 定位层次
-> 画出路径
-> 找状态和资源
-> 用证据验证
-> 选择修复方向

排障阶梯

Case 01:两个正数相加,结果变成负数

现象

某个计数、大小、偏移量或金额换算结果突然变成负数。

先定位

这不是“数学坏了”,而是固定位宽表示的问题。

  • 真实数学整数:无限范围
  • 机器整数:固定 bit 数
  • 超出范围:按位模式截断
  • 解释为有符号数:可能变负

相关章节

  • computer-organization/02-integer-complement-overflow.md
  • foundations/05-representation-boundaries.md

处理思路

  1. 查变量的位宽和有无符号。
  2. 找加法、乘法、长度计算、偏移计算。
  3. 判断中间结果是否已经溢出。
  4. 把边界输入写成测试:最大值、最小值、接近上限的乘法。
  5. 对外部输入做范围校验。

学到的底层

机器保存的是 bit。11111111 可以是 255,也可以是 -1,取决于解释规则。

Case 02:两个小数看起来相等,比较却失败

现象

程序里两个小数打印出来很接近,但直接比较不相等;多次累加后误差越来越明显。

先定位

二进制浮点只能精确表示一部分小数。很多十进制小数在二进制中是无限循环近似。

十进制小数
-> 二进制浮点近似
-> 运算和舍入
-> 结果存在微小误差

相关章节

  • computer-organization/03-floating-point.md
  • foundations/05-representation-boundaries.md

处理思路

  1. 判断该场景是否允许误差。
  2. 允许误差时,用 epsilon 范围比较。
  3. 不允许误差时,考虑整数化单位或定点表示。
  4. 避免把浮点结果直接作为关键相等条件。

学到的底层

浮点数解决的是“有限 bit 表示大范围实数近似”,不是精确十进制计算。

Case 03:同样是遍历,顺序访问明显更快

现象

两段程序复杂度都是 O(n),但顺序扫描比随机访问快很多。

先定位

复杂度只数操作次数,不直接描述数据移动成本。硬件按缓存行搬数据,顺序访问能利用空间局部性。

存储层次结构

相关章节

  • computer-organization/06-memory-hierarchy.md
  • computer-organization/07-cache.md
  • data-structures/01-complexity-memory.md

处理思路

  1. 判断数据是否连续存储。
  2. 看访问模式是顺序、跨步还是随机。
  3. 观察是否频繁 cache miss。
  4. 优先减少随机指针跳转和无谓复制。
  5. 对大数据结构,比较内存布局比只看复杂度更重要。

学到的底层

CPU 很快,内存相对慢。程序慢经常不是因为“不会算”,而是 CPU 在等数据。

Case 04:平均 O(1) 的哈希表突然卡了一下

现象

某次插入或查询突然明显变慢,平时都很快。

先定位

哈希表的平均 O(1) 依赖分布、负载因子和扩容策略。插入到某个阈值时可能触发 rehash。

元素增加
-> 负载因子升高
-> 冲突变多
-> 触发扩容
-> 旧元素重新分布
-> 单次操作成本抖动

相关章节

  • data-structures/05-hash-table.md
  • data-structures/12-selection-guide.md

处理思路

  1. 检查 key 分布是否均匀。
  2. 查看负载因子和扩容时机。
  3. 判断是否需要提前预估容量。
  4. 如果需要有序或范围查询,哈希表可能不是正确结构。
  5. 如果 key 可能被攻击性构造,要考虑最坏情况和防护。

学到的底层

平均复杂度不是最坏情况承诺。工程系统要关心尾延迟和抖动。

Case 05:任务越积越多,系统没有崩但越来越慢

现象

请求、任务、消息或 IO 事件在队列里越排越多,吞吐上不去,延迟持续增长。

先定位

这是生产速度大于消费速度。队列不是解决性能问题的魔法,它只是吸收短期速度差。

生产者速度 > 消费者速度
-> 队列长度增长
-> 等待时间增长
-> 超时和重试增加
-> 负载进一步上升

相关章节

  • data-structures/04-stack-queue.md
  • operating-systems/01-process-thread-cpu.md
  • operating-systems/11-performance-debugging.md

处理思路

  1. 测队列长度和增长速度。
  2. 分开看吞吐和延迟。
  3. 找消费者瓶颈:CPU、IO、锁、下游等待。
  4. 给队列设置容量、超时、丢弃或降级策略。
  5. 判断重试是否正在放大流量。

学到的底层

队列能平滑突发,不能消灭资源瓶颈。稳定系统必须有反压。

Case 06:程序卡住,CPU 也不高

现象

程序没有崩溃,CPU 占用也不高,但请求不动、任务不推进。

先定位

CPU 不高通常说明程序不是忙着计算,而是在等待。

OS 资源生命周期

相关章节

  • operating-systems/01-process-thread-cpu.md
  • operating-systems/04-concurrency-sync.md
  • foundations/08-concurrency-time-order.md

处理思路

  1. 看线程或任务状态:运行、睡眠、阻塞、等待锁。
  2. 画等待关系图:谁持有资源,谁在等资源。
  3. 查是否有死锁、条件变量漏唤醒、IO 阻塞。
  4. 检查超时设置,避免无限等待。
  5. 把共享状态的不变量写清楚。

学到的底层

并发问题经常不是“算错”,而是多个执行流对时间、顺序和资源的假设不一致。

Case 07:文件写完后崩溃,重启发现数据不完整

现象

程序显示写入成功,但机器异常退出后,文件内容缺失、结构损坏或只有部分更新。

先定位

文件写入经过应用缓冲、内核页缓存、文件系统、块设备。write 返回不等于数据已经持久落盘。

应用 write
-> 用户态缓冲
-> 内核页缓存
-> 文件系统元数据
-> 块设备
-> 持久介质

相关章节

  • operating-systems/05-file-system.md
  • computer-organization/08-bus-io.md

处理思路

  1. 区分写入成功、刷入内核、持久化完成。
  2. 关键数据使用原子替换、日志或校验。
  3. 写入后根据需求调用同步刷盘机制。
  4. 考虑崩溃发生在“写一半”的状态。
  5. 重启时设计恢复流程。

学到的底层

持久性不是自动得到的。文件抽象隐藏了设备细节,也隐藏了崩溃窗口。

Case 08:连接建立成功,但读取响应一直超时

现象

TCP 连接可以建立,但请求发出后长时间读不到响应。

先定位

connect 成功只证明 TCP 握手完成,不证明应用已经处理并返回。

网络分层路径

相关章节

  • computer-networks/06-tcp-basics.md
  • computer-networks/07-tcp-flow-congestion.md
  • computer-networks/12-network-troubleshooting.md

处理思路

  1. 区分 connect timeout 和 read timeout。
  2. 确认请求是否完整发送。
  3. 查看服务处理时间和上游等待。
  4. 检查响应是否被中间代理超时截断。
  5. 看是否有丢包、重传、接收窗口过小。

学到的底层

连接是传输层状态,响应是应用层结果。不能把两者混在一起判断。

Case 09:域名切换后,一部分用户仍访问旧地址

现象

权威 DNS 已经改到新 IP,但部分用户、部分地区或部分程序仍访问旧 IP。

先定位

DNS 是分布式缓存系统。TTL 没过期时,旧结果继续存在是正常现象。

权威记录变更
-> 递归解析器缓存未过期
-> 客户端或应用缓存未过期
-> 仍访问旧 IP

相关章节

  • computer-networks/08-dns.md
  • computer-networks/12-network-troubleshooting.md

处理思路

  1. 查权威 DNS 的当前记录。
  2. 查不同递归解析器返回的结果。
  3. 看旧记录 TTL。
  4. 检查应用运行时是否缓存 DNS。
  5. 重大切换前提前降低 TTL。

学到的底层

DNS 不是实时数据库。它用缓存换扩展性,用 TTL 平衡变更速度和查询压力。

Case 10:HTTPS 证书正确配置了,某些客户端仍失败

现象

部分客户端握手失败、提示证书不可信、协议不兼容或域名不匹配。

先定位

HTTPS 失败可能发生在 HTTP 请求到达应用之前。

DNS -> TCP connect -> TLS handshake -> HTTP request

相关章节

  • computer-networks/10-tls-https.md
  • computer-networks/11-proxy-gateway-load-balancer-cdn.md

处理思路

  1. 检查访问域名和证书 SAN 是否匹配。
  2. 检查证书链是否完整。
  3. 检查 SNI 是否让入口返回正确证书。
  4. 确认客户端支持的 TLS 版本和加密套件。
  5. 区分握手失败和 HTTP 错误。

学到的底层

TLS 同时解决身份认证、机密性和完整性。证书错误不是应用业务错误。

Case 11:504 偶发出现,但应用日志看不到请求

现象

用户看到 504,应用实例日志里没有对应请求。

先定位

504 往往来自网关、代理或 CDN,可能请求根本没到应用。

客户端
-> CDN
-> 负载均衡
-> 网关
-> 应用实例

相关章节

  • computer-networks/11-proxy-gateway-load-balancer-cdn.md
  • computer-networks/12-network-troubleshooting.md

处理思路

  1. 确认 504 是哪一层返回的。
  2. 查入口层日志,看是否转发上游。
  3. 检查健康检查是否把实例摘掉。
  4. 检查网关到上游的连接和超时。
  5. 如果请求到达应用,再查应用和下游耗时。

学到的底层

现代服务不是端到端直连。中间层也会返回状态码、缓存、重试和超时。

Case 12:看起来内存够,程序仍然被杀或抖动

现象

系统总内存看起来还有余量,但某个进程被终止,或者响应延迟突然抖动。

先定位

内存不是只有“总量”。还要看虚拟内存、物理页、页缓存、swap、资源限制、缺页。

相关章节

  • operating-systems/03-memory-management.md
  • operating-systems/10-virtualization-container.md
  • operating-systems/11-performance-debugging.md

处理思路

  1. 看进程实际内存、虚拟内存、共享内存。
  2. 看是否频繁缺页或 swap。
  3. 看运行环境是否有资源限制。
  4. 查对象数量、缓存增长、泄漏路径。
  5. 区分“内存泄漏”和“缓存策略没有上限”。

学到的底层

OS 管的是页、地址空间和资源边界。应用看到的内存现象是多个层次叠加的结果。

Case 13:同一功能,换了数据结构以后尾延迟变差

现象

平均耗时差不多,但 P95/P99 延迟更差。

先定位

平均复杂度可能掩盖最坏情况、扩容、冲突、锁竞争和缓存失效。

数据结构选择图

相关章节

  • data-structures/01-complexity-memory.md
  • data-structures/12-selection-guide.md
  • computer-organization/10-hardware-performance-model.md

处理思路

  1. 不只看平均值,看尾延迟。
  2. 找偶发慢点是否来自扩容、重平衡、冲突、GC 或 IO。
  3. 看数据规模和 key 分布是否变化。
  4. 评估是否需要预分配、分片、批量处理或换结构。

学到的底层

工程性能不是只追求平均复杂度。稳定性经常取决于最坏路径和偶发维护成本。

Case 14:一个 bug 只在并发时偶发出现

现象

单线程、单请求、手动复现都正常;并发运行一段时间后偶发错误。

先定位

并发问题的核心是交错执行。错误可能只出现在某种时间顺序下。

线程 A 读状态
线程 B 修改状态
线程 A 基于旧状态写回
不变量被破坏

相关章节

  • foundations/08-concurrency-time-order.md
  • operating-systems/04-concurrency-sync.md
  • foundations/09-debugging-observability.md

处理思路

  1. 找共享状态。
  2. 写出不变量。
  3. 判断读改写是否原子。
  4. 检查可见性和顺序保证。
  5. 用日志、断言、压力测试缩小交错范围。

学到的底层

并发 bug 不是随机事件,而是某种合法但未被设计处理的执行顺序。

总结:每个 case 最终都回到四个问题

资源是什么?
状态在哪里?
路径怎么走?
失败证据是什么?

四大件真正的价值,是让你能把一个模糊现象拆成可验证的机制问题。

延伸阅读