# 附录 a 核心参数与数量级速查


> 本附录是全书 36 章的参数与数量级收敛点，面试前用它做最后一轮记忆校准。

## 使用说明

本附录只做**速查**，不重复推导。每个数值都标注了口径或版本区间；机制细节回到对应章节。

| 你要找什么 | 去哪一节 | 对应正文章节 |
|---|---|---|
| 一次内存访问、一次 RTT 差多少倍 | A.1 | 第 1、3 章 |
| `swappiness`、`ulimit -n` 该不该调 | A.2 | 第 10、12 章 |
| RAID 写惩罚怎么算 | A.3 | 第 13 章 |
| `MSS` 为什么是 1460 | A.4 | 第 15、16、17 章 |
| InnoDB 一棵 B+ 树能放多少行 | A.5 | 第 20、23 章 |
| Redis 什么时候从 listpack 转哈希表 | A.6 | 第 27 章 |
| 32 GB 为什么是压缩指针的分界线 | A.7 | 第 32 章 |
| `acks=all` 到底保证了几份副本 | A.8 | 第 33、34 章 |
| 502 和 504 谁该重试 | A.9 | 第 18、35 章 |
| 最后一个小时背什么 | A.10 | 全书 |

> [!NOTE]
> 本附录的"默认值"一律指**上游项目的出厂值**，不是"生产推荐值"。两者的差距往往就是面试的追问点：面试官问"默认值多少"，接着一定问"你线上改成了多少、为什么"。

---

## A.1 延迟数量级表（面试心算基准）

单位统一为秒的十进制前缀：`1 ns = 10⁻⁹ s`、`1 μs = 10⁻⁶ s`、`1 ms = 10⁻³ s`。倍率列以 L1 缓存命中为 `1×` 的基准。

| 事件 | 典型耗时 | 相对倍率 | 量级直觉 |
|---|---|---|---|
| 寄存器访问 | < 1 ns | < 1× | 与 CPU 同频 |
| L1 缓存命中 | 约 1 ns | 1× | 约 4 个时钟周期（3 GHz） |
| 分支预测失败 | 约 5 ns | 5× | 流水线冲刷，重新取指 |
| L2 缓存命中 | 约 4 ns | 4× | 约十几周期 |
| L3 缓存命中 | 约 15 ns | 15× | 跨核共享，含一致性开销 |
| 主存访问（DRAM） | 约 100 ns | 100× | 缓存未命中的代价 |
| 互斥锁加解锁（无竞争） | 约 20 ns | 20× | 一条原子指令 + 分支 |
| 系统调用（如 `getpid`） | 约 100 ns ~ 1 μs | 100~1000× | 用户态/内核态切换 |
| 上下文切换 | 约 1~5 μs | 1000~5000× | 换页表 + 寄存器 + 缓存污染 |
| 同机房网络往返（RTT） | 约 0.1~0.5 ms | 10⁵× | 约等于 1000~5000 次主存访问 |
| SSD 随机读（4 KB） | 约 100 μs | 10⁵× | NVMe 更快，见 A.3 |
| 机械盘随机读（4 KB） | 约 5~10 ms | 10⁷× | 一次寻道 + 旋转延迟 |
| 跨大洲网络往返 | 约 150 ms | 10⁸× | 光速 + 路由跳数 |

常用换算（面试官爱让现场算）：

```text
1 次同机房调用 ≈ 0.3 ms ≈ 300 000 ns
1 次同机房调用 ≈ 3000 次主存访问 ≈ 300 000 次 L1 缓存命中
1 次同机房调用 ≈ 3 次 SSD 随机读（0.1 ms × 3）
1 次机械盘随机读 ≈ 20 次同机房调用
1 次跨大洲调用 ≈ 300 次同机房调用

内存访问墙：约 100 ns
网络墙：同机房约 0.1 ms，跨地域 ≥ 30 ms（光速下限）
磁盘墙：SSD 约 100 μs，机械盘约 10 ms
```

> [!IMPORTANT]
> 面试里最有杀伤力的一句话是：**"同机房一次 RPC 的时间，等价于三千次内存访问。"** 它直接解释了为什么"循环里调 RPC"（N+1 查询、循环发 MQ）是不可接受的，也解释了为什么本地缓存能把 P99 从毫秒级压到微秒级。

> [!TIP]
> 心算口诀：主存 100 ns、SSD 100 μs、同机房 0.3 ms、机械盘 10 ms、跨洲 150 ms。相邻两档普遍差 **1000 倍**（主存→SSD→同机房），只有同机房→机械盘是约 30 倍。

---

## A.2 Linux 内核参数与资源限制默认值

以下默认值以 Linux 5.x/6.x 主线内核为口径；发行版（尤其是 RHEL 系与容器镜像）常带自己的 `sysctl` 覆盖，容器里看到的可能是宿主机或 runtime 注入的值。

| 参数 | 默认值 | 含义 | 调它解决什么问题 | 调错的风险 |
|---|---|---|---|---|
| `vm.swappiness` | 60 | 换出匿名页相对回收文件页的权重（0~100） | 数据库/缓存服务想优先保留匿名页，降到 1~10 | 设 0 并非禁用 swap，内存紧张时仍可能换出；设太高会让热数据被换出，延迟抖动 |
| `vm.overcommit_memory` | 0 | 启发式过量提交：明显超额的分配才拒绝 | Redis `fork` 做 RDB 时被 `fork` 失败或 OOM 打断，可设 1 | 设 1（总是允许）会让 `malloc` 成功但触碰时才 OOM，进程被 OOM killer 杀掉 |
| `net.core.somaxconn` | 4096（5.4+；更早版本 128） | `listen` 全连接队列上限 | 高并发短连接下 `SYN` 之后被丢、客户端超时 | 调大但应用 `listen(backlog)` 传得小，实际仍取 `min`；积压请求占用内存 |
| `net.ipv4.tcp_max_syn_backlog` | 随内存变化（常见 256~1024，部分发行版 4096） | 半连接（SYN 队列）上限 | 抗 SYN Flood、抗突发建连 | 只调它不调 `somaxconn` 会把瓶颈从半连接挪到全连接队列 |
| `net.ipv4.tcp_syn_retries` | 6（约 127 s 后放弃） | 主动建连 SYN 重传次数 | 缩短上游不可达时的建连等待 | 调太小会在网络抖动时误判服务不可用 |
| `net.ipv4.tcp_synack_retries` | 5 | 被动方回 `SYN+ACK` 的重传次数 | 与 `tcp_max_syn_backlog` 配合抗 flood | 调太小会让慢客户端连不上 |
| `net.ipv4.tcp_tw_reuse` | 0（关闭） | 允许把 `TIME_WAIT` 套接字复用于**主动**新建连（需时间戳） | 客户端侧大量 `TIME_WAIT` 导致本地端口耗尽 | 在 NAT 设备后或时间戳被剥离时可能收到旧连接的迟到包 |
| `net.ipv4.tcp_tw_recycle` | 已从 4.12 起移除 | 曾用于快速回收 `TIME_WAIT` | —— | 不要再引用它；在 NAT 环境下会导致连接被静默丢弃 |
| `ulimit -n`（软限制） | 1024 | 单进程可打开的文件描述符数 | 高并发下 `accept` 报 `EMFILE`、"Too many open files" | 软限制提高但硬限制没提，进程无法自行再提高；`select` 的 1024 上限是另一回事 |
| `ulimit -n`（硬限制） | 常见 1048576（发行版默认 `nofile`），旧系统 4096 | 非 root 进程能提到和上限 | 给软限制留出提升空间 | 只改软限制是暂时的，重启或新会话后失效，需写进 `limits.conf`/systemd `LimitNOFILE` |
| `fs.file-max` | 随内存自动计算（通常远大于 10 万） | 全系统 fd 总数上限 | 单机跑大量进程时全局 fd 耗尽 | 调太小会让提高 `ulimit -n` 无效，因为全局池先满 |
| `vm.dirty_ratio` | 20（百分比） | 脏页占内存达到该值，写进程**同步**回写并阻塞 | 削峰：降低它让回写更早、单次阻塞更短 | 调太低会让写吞吐下降；调太高会造成长时间卡顿式回写 |
| `vm.dirty_background_ratio` | 10（百分比） | 达到该值，后台线程开始异步回写 | 让回写更早启动，避免触顶 | 调太低会频繁唤醒回写线程，与 `dirty_ratio` 靠太近会失去缓冲 |
| `vm.max_map_count` | 65530 | 单进程内存映射区数量上限 | Elasticsearch 等大量 `mmap` 的场景启动报错 | 调太大会增加内核 `vm_area_struct` 内存开销 |
| `net.ipv4.ip_local_port_range` | `32768 60999` | 主动连接可用的本地临时端口范围 | 客户端侧短连接 QPS 高、端口耗尽 | 范围过大且 `tcp_tw_reuse` 关闭时会堆积更多 `TIME_WAIT`；扩大范围不影响其他进程已占用端口 |

> [!WARNING]
> "提高 `ulimit -n` 就能解决 Too many open files" 是常见错误回答。要同时检查：**软限制**、**硬限制**、`fs.file-max`、进程是否被 systemd 的 `LimitNOFILE` 覆盖，以及是不是 fd 泄漏（用 `lsof -p <pid> | wc -l` 看是不是只增不减）。见第 10 章与第 13 章。

相关观测命令（口径要对）：

```bash
# 看进程真正的软/硬限制，而不是当前 shell 的
cat /proc/<pid>/limits | grep -i 'open files'

# 看内核实际生效的 somaxconn 与队列溢出计数
sysctl net.core.somaxconn
nstat -az TcpExtListenOverflows TcpExtListenDrops

# 脏页回写相关
sysctl vm.dirty_ratio vm.dirty_background_ratio vm.dirty_expire_centisecs
```

---

## A.3 磁盘与文件系统

### A.3.1 ext4 与 xfs 速查

| 维度 | ext4 | xfs |
|---|---|---|
| 设计取向 | 通用、成熟、工具链老牌 | 大文件、大容量、并行分配 |
| inode 分配 | 建文件系统时预分配固定数量（可 `-i` 调比例） | 动态分配，不易凭空耗尽 inode |
| 在线扩容 | 支持（`resize2fs`），缩容也支持 | 支持扩容，**不支持缩容** |
| 元数据规模 | 大目录下性能下降明显 | B+ 树目录，大目录更稳 |
| 删除大文件 | 相对慢 | 相对快（延迟回收） |
| 典型场景 | 系统盘、小文件多 | 数据盘、大数据量、容器宿主机 |

### A.3.2 容量与 inode 观测

| 命令 | 看什么 | 关键点 |
|---|---|---|
| `df -h` | 块容量使用率 | 分母是数据块，与 inode 无关 |
| `df -i` | inode 使用率 | **inode 用满时 `df -h` 可能显示还有空间，但创建文件报 `No space left on device`** |
| `du -sh <dir>` | 目录实际占用 | 与 `df` 不一致的典型原因：文件已 `rm` 但被进程持有 |
| `lsof +L1` | 已删除仍被打开的文件 | 定位"删了文件空间没释放" |

创建 inode 数量与"平均文件大小"相关：

```text
ext4 默认 inode 数 ≈ 卷容量 / 16 KB（bytes-per-inode 默认 16384）
例：1 TB 卷 ≈ 6553.6 万个 inode
放 100 万个小文件绰绰有余；放十亿级小文件必须显式调小 -i 或改用 xfs
```

### A.3.3 常用挂载选项

| 选项 | 作用 | 副作用 / 注意 |
|---|---|---|
| `noatime` | 不更新访问时间 | 依赖 `atime` 的工具有影响；通常收益大于风险 |
| `nodiratime` | 只不更新目录访问时间 | 比 `noatime` 保守 |
| `relatime` | 访问时间早于修改时间才更新（现代默认） | 折中方案 |
| `data=ordered` | 数据先落盘再提交元数据（ext4 默认） | 崩溃后不会读到"指向垃圾数据的元数据" |
| `data=writeback` | 不保证数据先于元数据 | 吞吐略好，崩溃后可能读到旧数据 |
| `barrier=1`/`nobarrier` | 是否发写屏障保证顺序 | 关闭要有电池保护 RAID 卡才安全 |
| `noexec`/`nosuid` | 禁止执行/忽略 setuid | 安全加固常用 |
| `discard` / `fstrim` | 向 SSD 通报无效块 | 挂载期 `discard` 有开销，常改为定期 `fstrim` |

### A.3.4 RAID 级别对照（含写惩罚）

**写惩罚（write penalty）** 指一次逻辑随机写引发的物理 I/O 次数，是评估 RAID 随机写 IOPS 的核心：

| 级别 | 容量利用率 | 容错能力 | 随机写惩罚 | 恢复期风险 | 典型用途 |
|---|---|---|---|---|---|
| RAID 0 | 100%（N 块全用） | 无，坏一块全丢 | 1 | —— | 临时数据、缓存盘 |
| RAID 1 | 50% | 允许坏 1 块 | 2 | 低 | 系统盘 |
| RAID 5 | `(N−1)/N` | 允许坏 1 块 | **4**（读旧数据 + 读旧校验 + 写新数据 + 写新校验） | 高（重建期再坏即全丢） | 读多写少的归档 |
| RAID 6 | `(N−2)/N` | 允许坏 2 块 | **6** | 中 | 大容量冷数据 |
| RAID 10 | 50% | 每镜像组可坏 1 块（视布局） | **2** | 低（重建只拷一份） | 数据库、高并发随机写 |

写惩罚换算有效 IOPS：

```text
RAID5 有效随机写 IOPS ≈ 物理盘 IOPS 总和 / 4
RAID6 有效随机写 IOPS ≈ 物理盘 IOPS 总和 / 6
RAID10 有效随机写 IOPS ≈ 物理盘 IOPS 总和 / 2

算例：8 块盘，单盘 150 随机写 IOPS
RAID5  ≈ (8 × 150) / 4 = 300 IOPS
RAID6  ≈ (8 × 150) / 6 = 200 IOPS
RAID10 ≈ (8 × 150) / 2 = 600 IOPS
结论：数据库随机写场景，RAID10 常以一半容量换两倍以上写 IOPS
```

### A.3.5 IOPS、块大小与队列深度

| 概念 | 公式 / 口径 | 说明 |
|---|---|---|
| 吞吐 | `吞吐 = IOPS × 块大小` | 顺序大块 I/O 看吞吐，随机小块看 IOPS |
| 顺序 vs 随机 | 机械盘差 100 倍以上，SSD 差距缩小 | 用 `fio` 时必须声明 `--bs`、`--rw`、`--iodepth` |
| 队列深度（QD） | `QD = IOPS × 平均延迟`（Little 定律） | QD 越高越能压满设备，但延迟上升 |
| 观测 | `iostat -x` 的 `aqu-sz`（平均队列长度）、`await` | `%util` 接近 100% 不等于"设备已饱和"，见第 11 章 |
| 树化阈值式的口径差异 | SSD 的 4 KB 随机读与 128 KB 顺序读是两种设备 | 报价单上的 IOPS 必须问清块大小与 QD |

### A.3.6 SSD 写放大与 TRIM

| 概念 | 说明 |
|---|---|
| 写放大（write amplification, WA） | `WA = 实际写入闪存的数据量 / 主机请求写入的数据量`；WA 越大寿命越短 |
| 成因 | 闪存只能整块擦除，改一个 4 KB 页可能触发整块（通常 256 KB~几 MB）搬移与重写 |
| 缓解 | 预留空间（over-provisioning）、对齐分区、避免大量随机小写、TRIM 让主控提前知道哪些块可回收 |
| TRIM | `discard` 挂载选项或定期 `fstrim`；不 TRIM 会让 SSD 误以为数据仍有效，WA 上升 |
| 磨损均衡 | 主控把写分散到全部块上，寿命看 TBW（总写入字节数）而非通电时间 |
| 与数据库的关系 | 随机写模式最伤 SSD；redo/binlog 的顺序写远比数据页随机写友好 |

> [!CAUTION]
> 说"RAID5 随机写只要 2 次 I/O"会当场暴露没算过写惩罚。记住：**RAID5 是 4 次、RAID6 是 6 次、RAID10 是 2 次**；这也是"数据库盘为什么不用 RAID5"的标准答案。

---

## A.4 TCP/IP 参数与常量

### A.4.1 报文头与 MTU/MSS

| 常量 | 数值 | 说明 |
|---|---|---|
| 以太网 MTU | 1500 字节 | 链路层最大传输单元 |
| IP 头最小长度 | 20 字节 | 无选项时；有选项最长 60 字节 |
| TCP 头最小长度 | 20 字节 | 无选项时；有选项最长 60 字节 |
| MSS（IPv4 常用值） | `1500 − 20 − 20 = 1460` 字节 | 分段发生在传输层，分片发生在网络层 |
| MSS（含时间戳选项） | 常见协商为 1448 或更小 | 每方向独立协商，取 `min(本端, 对端)` |
| 最小 MSS | 536 字节（IPv4）/ 1220 字节（IPv6） | 保证任何链路都能通过 |
| IP 分片能力发现 | PMTUD，`DF` 位置 1 | 黑洞路由会导致"小请求通、大请求挂" |
| TCP 窗口字段 | 16 位，最大 65535 字节 | 窗口扩大因子（window scale）最大 14，即 `65535 × 2¹⁴` |

**为什么必须做窗口扩大**：带宽时延积（BDP）决定"在途数据量"上限。

```text
BDP = 带宽 × RTT
100 Mbps × 100 ms = 10 Mb = 1.25 MB 在途
若窗口上限只有 64 KB，则吞吐上限 = 64 KB / 100 ms ≈ 5.2 Mbps
⟹ 链路再快也跑不满，所以 RFC 1323 引入窗口扩大因子

同一链接下的理论上限 = 窗口大小 / RTT
想跑满 1 Gbps、RTT 100 ms，需要窗口 ≈ 12.5 MB，远超 64 KB
```

### A.4.2 `TIME_WAIT` 与 MSL

| 项 | 取值 | 说明 |
|---|---|---|
| `TIME_WAIT` 时长 | `2 × MSL` | 主动关闭方进入，等两个方向旧报文全部消亡 |
| MSL 常见取值 | Linux 中 `TCP_TIMEWAIT_LEN` 为 60 s，即 `TIME_WAIT` 约 60 s | Linux 不直接用 RFC 建议的 2 min MSL，而是固定 60 s |
| RFC 793 建议 MSL | 2 min（`TIME_WAIT` 即 4 min） | 引用规范时用 2 min，引用 Linux 行为时用 60 s |
| 端口复用条件 | `tcp_tw_reuse` + 时间戳 + 主动建连 | 只对客户端出向连接有意义 |
| 大量 `TIME_WAIT` 出现在服务端 | 通常是服务端主动关闭连接 | 让客户端先关（HTTP `keep-alive` + 超时对齐）或收敛连接数 |

### A.4.3 RTO 与重传

| 项 | 取值 | 说明 |
|---|---|---|
| RTO 初值 | 1 s（RFC 6298） | 首个 SYN 与未取得 RTT 样本时用 |
| RTO 最小 | 200 ms（Linux 实现） | 防止抖动的 RTT 样本把 RTO 压得过小 |
| RTO 退避 | 每次超时翻倍，上限常见 120 s | `tcp_syn_retries = 6` 时首次建连约在 127 s 后放弃 |
| 平滑公式 | `SRTT = (1 − α) × SRTT + α × RTT`，`α = 1/8` | 指数加权移动平均 |
| 偏差估计 | `RTTVAR = (1 − β) × RTTVAR + β × |SRTT − RTT|`，`β = 1/4` | 度量抖动 |
| 重传超时 | `RTO = SRTT + 4 × RTTVAR` | 系数 4 保证约 99% 的样本被覆盖 |
| `RTO.min`/`RTO.max` | 内核可调，常见 200 ms / 120 s | 与快速重传配合，避免长尾卡顿 |

### A.4.4 拥塞窗口与阈值初值

| 项 | 早期取值 | 现代取值 | 影响 |
|---|---|---|---|
| `cwnd` 初值 | 1 个 MSS（RFC 2001） | 10 个 MSS（RFC 6928，IWN10） | 同样 RTT 下首轮可发数据量差 10 倍，显著缩短短流完成时间 |
| `ssthresh` 初值 | 65535 字节（RFC 2001） | Linux 取一个很大的值（近似无穷，首个丢包才确定） | 决定了慢启动何时切到拥塞避免 |
| 慢启动行为 | 每个 RTT 翻倍（`cwnd = cwnd + MSS` 每 ACK） | 同 | 指数增长，直到 `cwnd ≥ ssthresh` |
| 拥塞避免行为 | 每 RTT 加 1 个 MSS（`cwnd = cwnd + MSS × MSS / cwnd`） | 同 | 线性增长，AIMD 的加性增 |
| 快速重传触发 | 3 个重复 ACK | 同；配合 SACK 可减少重传量 | 不必等超时 |
| 快恢复（Reno） | `ssthresh = cwnd / 2`，`cwnd = ssthresh` | NewReno 改进了部分确认的处理 | 乘法减 |
| 超时重传后 | `ssthresh = cwnd / 2`，`cwnd` 回 1 MSS | 同（部分算法回 10 MSS 以内） | 回到慢启动 |

拥塞控制算法要点对照：

| 算法 | 丢包判据 | 增长规律 | 核心思想 | 已知短板 |
|---|---|---|---|---|
| Tahoe | 超时或 3 个重复 ACK | 回到 `cwnd = 1` | 最早的完整实现 | 恢复太慢 |
| Reno | 3 个重复 ACK 触发快重传 | 慢启动 + 拥塞避免 | 提出快重传/快恢复 | 一个窗口多个丢包时反复减半 |
| NewReno | 改进了部分 ACK 判定 | 同 Reno | 无 SACK 也能恢复一个窗口内的多丢包 | 仍以丢包为唯一信号 |
| CUBIC | 丢包 | `cwnd` 按三次函数增长，先快后慢 | 以时间为轴，RTT 公平性更好 | 高丢包长肥管道上仍偏保守 |
| BBR | 不把丢包当拥塞，看带宽与最小 RTT | 探测 `BtlBw` 与 `RTprop` | 主动探测瓶颈带宽 | 与基于丢包的流共存时可能不公平 |

正确口径：`cwnd` 初值 **1 个 MSS** 出自 RFC 2001，**10 个 MSS** 出自 RFC 6928（2013 年）。面试答"1 个 MSS"不算错，但必须说清这是旧口径，并补一句"现代实现按 RFC 6928 用 10 个 MSS"。

---

## A.5 MySQL / InnoDB 默认值与关键常量

### A.5.1 结构常量

| 项 | 默认值 / 取值 | 说明 |
|---|---|---|
| 页大小（`innodb_page_size`） | 16 KB | 数据页、索引页、undo 页统一口径 |
| 区（extent） | 1 MB = 64 个页 | 连续页的分配单位，减少随机 I/O |
| 段（segment） | 由若干区组成 | 每张表至少一个数据段 + 若干索引段 |
| 行格式 | 5.7 默认 `COMPACT`，8.0 默认 `DYNAMIC` | `DYNAMIC` 更依赖溢出页 |
| 主键 | 聚簇索引，叶子节点存整行 | 无主键时用唯一非空索引，再退化到隐藏 `row_id` |

### A.5.2 关键参数与丢数据窗口

| 参数 | 默认值 | 含义 | 丢数据窗口 |
|---|---|---|---|
| `innodb_buffer_pool_size` | 128 MB | 缓存数据页与索引页（兼作自适应哈希） | 不涉及持久性；生产常设物理内存 50%~70% |
| `innodb_lock_wait_timeout` | 50 s | 行锁等待超时 | 超时抛 `Lock wait timeout exceeded`，事务不一定回滚 |
| `innodb_flush_log_at_trx_commit` | 1 | redo log 的刷盘策略 | `1`：每次提交都 `fsync`，几乎不丢；`0`：每秒刷一次，崩掉最多丢 1 s；`2`：写 OS 缓存，OS 崩溃丢、进程崩溃不丢 |
| `sync_binlog` | 1 | binlog 刷盘策略（8.0 默认 1） | 与上者组合决定"提交但未落盘"的窗口；`0` 时由 OS 决定 |
| `innodb_log_buffer_size` | 16 MB | redo log 内存缓冲 | 太大浪费，太小导致频繁刷 |
| `innodb_redo_log_capacity` | 100 MB（**8.0.30 起**） | redo 日志总容量上限 | 替代 8.0.30 之前的 `innodb_log_file_size`（默认 48 MB）× `innodb_log_files_in_group`（默认 2） |
| `innodb_io_capacity` | 200 | 后台刷脏的 IOPS 预算 | 设太小会让刷脏跟不上，脏页堆积 |
| `innodb_flush_neighbors` | 1（机械盘有利，SSD 建议 0） | 刷脏页时是否顺带刷相邻页 | 与 SSD 随机写特性冲突 |
| `transaction_isolation` | REPEATABLE READ | 默认隔离级别 | 注意 PostgreSQL 默认是 READ COMMITTED |
| `max_connections` | 151 | 最大连接数 | 连接是线程，过多会上下文切换 |
| `table_open_cache` | 2000（8.0） | 打开表缓存 | 与 fd 上限联动 |

一处高发口径错误：在 MySQL **8.0.30 之后**仍说 `innodb_log_file_size` 默认 48 MB 属于过时回答。新参数是 `innodb_redo_log_capacity`，默认 **100 MB**，它把多个 redo 文件统一成容量管理；老参数在 8.0.30 起被弃用。

### A.5.3 redo log 环形结构与两阶段提交

```text
redo log 环形结构（write pos 追着 checkpoint 跑）

       checkpoint                       write pos
          ↓                                ↓
  ┌───────┬────────────┬────────────┬──────────┐
  │ 已刷盘 │  可覆盖区域  │  待刷脏页区  │ 可写入区  │
  └───────┴────────────┴────────────┴──────────┘
          └──── 已写入但未刷脏的数据页 ────┘

write pos 追上 checkpoint ⟹ 无处可写 ⟹ 必须停下来推 checkpoint（刷脏页）
⟹ 这是"redo 太小导致写入抖动"的根因
```

两阶段提交时序（`innodb_flush_log_at_trx_commit = 1`、`sync_binlog = 1`）：

```text
阶段一：redo prepare
  1. 写 redo log，标记 prepare
  2. 刷 redo（fsync）
阶段二：写 binlog
  3. 写 binlog
  4. 刷 binlog（fsync）
  5. 写 redo log，标记 commit（不需要立刻 fsync，靠组提交合并）

崩溃点分析：
  A. 步骤 2 之前崩：redo 无记录、binlog 无记录 ⟹ 事务丢失，一致
  B. 步骤 2 之后、步骤 4 之前崩：
     redo 有 prepare 但无 commit，binlog 不完整
     ⟹ 恢复时发现 redo prepare 但 binlog 缺失 ⟹ 回滚该事务
  C. 步骤 4 之后、步骤 5 之前崩：
     binlog 完整，redo 处于 prepare
     ⟹ 恢复时按 binlog 补提交（commit 该事务）
  判定依据：redo 的 XID 是否出现在 binlog 中
```

### A.5.4 B+ 树树高估算算例

假设：主键 `BIGINT` 占 8 字节，页内指针占 6 字节，页头/页尾等开销约 100 字节（按 16 KB 页扣减）。

```text
单页可容纳的键数量：
  每对 (键, 指针) = 8 + 6 = 14 字节
  (16384 − 100) / 14 ≈ 1163，取整约 1170 个键（含页目录等开销的常见口径）

叶子层：每行按 1 KB 估算，一页放 16 行

树高（h 层）可索引的行数：
  非叶层数 = h − 1，每层放大 1170 倍
  h = 1：16 行
  h = 2：1170 × 16 ≈ 1.87 万行
  h = 3：1170 × 1170 × 16 ≈ 2190 万行
  h = 4：约 256 亿行

结论：三层 B+ 树可索引约 2000 万行量级。
查询一次主键等值：3 次页访问；根节点常驻 buffer pool，实际磁盘 I/O 通常只有 0~1 次。
```

### A.5.5 `key_len` 计算所需的字段宽度

| 类型 / 情形 | 占用字节 | 说明 |
|---|---|---|
| `TINYINT` | 1 | 有符号同宽 |
| `SMALLINT` | 2 | |
| `MEDIUMINT` | 3 | |
| `INT` | 4 | |
| `BIGINT` | 8 | |
| `FLOAT` | 4 | |
| `DOUBLE` | 8 | |
| `DATE` | 3 | |
| `TIME` | 3 | 不含小数秒 |
| `DATETIME` | 5（旧版 8） | MySQL 5.6.4 起 `DATETIME` 不再固定 8 字节 |
| `TIMESTAMP` | 4 | |
| `CHAR(n)` | `n × 字符集字节数` | `utf8mb4` 下每字符 4 字节 |
| `VARCHAR(n)` | `n × 字符集字节数 + 2` | 变长长度前缀 2 字节 |
| 可空列 | **额外 +1** | 需要 1 字节标记 `NULL` |
| `utf8mb4` 单字符 | 最多 4 字节 | `utf8mb3`/`utf8` 为 3 字节 |
| 联合索引只用前 k 列 | 各列 `key_len` 相加 | 用于判断到底用到了联合索引的几列 |

```text
算例：KEY idx_a (a INT NOT NULL, b VARCHAR(20) NULL) CHARSET utf8mb4
  只用 a：                       4
  a + b：                        4 + (20 × 4 + 2) + 1 = 87
  EXPLAIN 的 key_len = 4  ⟹ 只用到 a
  EXPLAIN 的 key_len = 87 ⟹ a、b 都用到
```

> [!TIP]
> `key_len` 是"有没有把联合索引用满"的**唯一客观证据**，比 `Extra` 里的 `Using index condition` 更直接。记住三个常数：**可空 +1、`VARCHAR` +2、`utf8mb4` ×4**。

---

## A.6 Redis 默认配置与阈值

### A.6.1 编码转换阈值

| 参数 | 默认值 | 触发效果 |
|---|---|---|
| `hash-max-listpack-entries` | 128 | Hash 字段数 ≤ 128 且值不大时用 listpack |
| `hash-max-listpack-value` | 64 | 单个值超过 64 字节即转 hashtable（超阈值才转） |
| `zset-max-listpack-entries` | 128 | ZSet 元素数阈值 |
| `zset-max-listpack-value` | 64 | ZSet 成员长度阈值 |
| `list-max-listpack-size` | 128 | 也可写负数表示字节数上限（如 `-2` 表示 8 KB） |
| `set-max-intset-entries` | 512 | 全整数集合且元素数 ≤ 512 用 intset |
| `set-max-listpack-entries` | 128 | 7.2 起 Set 用 listpack 而非 ziplist |
| `hash-max-ziplist-entries` | 128 | 7.x 前的老名字，语义与 listpack 版一致 |

| String 分界 | 阈值 | 编码 |
|---|---|---|
| embstr 上限 | 44 字节 | ≤ 44 字节用 embstr（与 `redisObject` 一次分配） |
| raw | > 44 字节 | 单独分配 SDS |
| int | 可解析为 `long` 且不超范围 | 直接存整数值，不存字符串 |
| Redis 4.0 起 | `sdshdr5/8/16/32/64` | 按字符串长度选头结构，省内存 |

```text
为什么是 44 字节：
  jemalloc 最小分配档位 64 字节
  redisObject = 16 字节（类型 + 编码 + LRU + 引用计数 + 指针）
  64 − 16 = 48，SDS 头需 3 字节 + 结尾 '\0' 1 字节 ⟹ 可用 44 字节
```

### A.6.2 持久化

| 参数 | 默认值 | 含义 | 风险 |
|---|---|---|---|
| `appendfsync` | `everysec` | AOF 刷盘策略 | `always` 最安全最慢；`no` 交给 OS，宕机可能丢数十秒 |
| `save` | `3600 1 300 100 60 10000` | RDB 自动快照触发条件 | 频率过高会频繁 `fork`，影响延迟 |
| `auto-aof-rewrite-percentage` | 100 | AOF 增长到上次重写后的一倍就重写 | 重写期间写放大 |
| `aof-use-rdb-preamble` | yes（4.0+） | 混合持久化：RDB 头 + AOF 增量 | 兼容性依赖版本 |
| `rdbcompression` | yes | RDB 使用 LZF 压缩 | CPU 换空间 |

### A.6.3 内存淘汰策略（八种）

| 策略 | 淘汰范围 | 淘汰依据 |
|---|---|---|
| `noeviction` | 不淘汰 | 写入直接报错（默认值） |
| `volatile-lru` | 只含过期时间的键 | 最近最少使用 |
| `volatile-lfu` | 只含过期时间的键 | 最少使用频率（4.0+） |
| `volatile-random` | 只含过期时间的键 | 随机 |
| `volatile-ttl` | 只含过期时间的键 | 剩余生存时间最短优先 |
| `allkeys-lru` | 全部键 | 最近最少使用 |
| `allkeys-lfu` | 全部键 | 最少使用频率 |
| `allkeys-random` | 全部键 | 随机 |

### A.6.4 集群、统计与过期删除

| 项 | 取值 | 说明 |
|---|---|---|
| Cluster 槽位数 | 16384 | `CRC16(key) mod 16384`；位图仅 2 KB，心跳成本低 |
| 集群最少主节点 | 3（推荐 3 主 3 从） | 与 16384 / 3 的整数分配有关 |
| 转发错误 | `MOVED`（槽已迁移完成）/ `ASK`（槽正在迁移） | 客户端必须处理重定向 |
| HyperLogLog 误差 | 0.81%（标准误差） | 固定 12 KB，可用 `PFCOUNT` 合并多键 |
| HyperLogLog 稀疏编码上限 | `hll-sparse-max-bytes` 默认 3000 | 超过就转稠密编码 |
| 惰性删除 | 访问键时检查是否过期 | 过期键不被访问就永远不删 |
| 定期删除 | 默认每 100 ms 执行，随机抽样过期键 | `hz` 默认 10，即每秒 10 次 |
| 抽样比例 | 每轮从过期字典抽样检查，删除比例超过 25% 就继续本轮 | 防止过期键长期占用内存 |
| 单线程模型 | 6.0 起网络 I/O 多线程，命令执行仍单线程 | 慢命令会阻塞全部请求 |
| 大 key 阈值（经验） | String > 10 KB、集合元素 > 5000 | 无官方硬阈值，以 `--bigkeys` 输出为准 |

另一个容易漏的默认值：`maxmemory` 默认是 **0（不限制）**，此时淘汰策略形同虚设 —— 内存会一直涨到被 OOM killer 杀掉。上线必设 `maxmemory` + `maxmemory-policy`，这是缓存服务最容易漏的一步。

---

## A.7 JVM 默认值与关键常量

### A.7.1 GC 与堆

| 项 | 版本口径 | 默认值 / 语义 |
|---|---|---|
| 服务端默认 GC | JDK 8 | Parallel Scavenge + Parallel Old（吞吐优先） |
| 默认 GC | JDK 9 ~ JDK 20 | G1 |
| 默认 GC | JDK 21+ | 分代 ZGC（开启分代模式后为默认低延迟收集器） |
| `-Xmx` 未设置 | 所有版本 | 物理内存的 1/4 |
| `-Xms` 未设置 | 所有版本 | 物理内存的 1/64（早期实现口径） |
| `SurvivorRatio` | 默认 8 | `Eden : Survivor = 8 : 1`（单个 Survivor） |
| `MaxTenuringThreshold` | 默认 15 | 晋升年龄上限；对象头年龄位只有 4 位 |
| 动态年龄判定 | 默认开启 | Survivor 中同年龄对象总和超过一半即全部晋升 |
| `MetaspaceSize` | 默认约 21 MB | **首次触发元空间 GC 的阈值**，不是固定大小 |
| `MaxMetaspaceSize` | 默认不限（`-1`） | 不设就会一直涨到本地内存耗尽 |
| 压缩指针（Compressed Oops） | 默认开启（堆 ≤ 约 32 GB） | 对象引用 4 字节，`MaxHeapSize` 超过约 32 GB 后自动失效 |
| `TLAB` | 默认开启 | 线程本地分配缓冲，减少分配竞争 |
| 大对象阈值 | `-XX:PretenureSizeThreshold` 默认 0（不启用） | 设了才直接进老年代 |
| `CMSInitiatingOccupancyFraction` | 老年代占比阈值 | 仅 CMS 关注，G1/ZGC 无此参数 |

```text
32 GB 压缩指针分界线的由来：
  压缩指针用 4 字节表示引用，寻址单位是 8 字节（对象对齐）
  2³² × 8 字节 = 32 GB
  ⟹ 堆 ≤ 32 GB 时可寻址全部对象，引用只占 4 字节，缓存友好
  ⟹ 堆 > 32 GB 时改用 8 字节引用，同样的对象图占用更多内存
  ⟹ 所以 31 GB 常比 33 GB 更划算："32 GB 不是上限，是拐点"
  补充：可用 -XX:ObjectAlignmentInBytes=16 把上限提到 64 GB，但有对齐浪费
```

### A.7.2 集合与常量

| 项 | 值 | 说明 |
|---|---|---|
| `Integer` 缓存 | `-128 ~ 127` | 由 `IntegerCache` 实现，可用 `-XX:AutoBoxCacheMax` 扩大上界 |
| `Long`/`Short`/`Byte` 缓存 | `-128 ~ 127` | 规则固定，不可通过参数扩大 |
| `Boolean` 缓存 | `TRUE`/`FALSE` 两个实例 | 无需缓存数组 |
| `Character` 缓存 | `0 ~ 127` | |
| `HashMap` 默认容量 | 16 | 首次 `put` 时才分配（延迟初始化） |
| `HashMap` 负载因子 | 0.75 | 时间与空间的折中；泊松分布下桶冲突概率 |
| `HashMap` 树化阈值 | 8 | 单桶链表长度达到 8 且表容量 ≥ 64 才树化 |
| `HashMap` 退化阈值 | 6 | 树节点数降到 6 退回链表，避免反复转换 |
| `HashMap` 最小树化容量 | 64 | 表容量小于 64 时只扩容，不树化 |
| `HashMap` 容量上限 | `1 << 30` | 超过后不再扩容，负载因子形同失效 |
| `ArrayList` 默认容量 | 10（首次 `add` 分配） | 扩容约 1.5 倍 |
| `ConcurrentHashMap` 并发模型 | 无锁读 + `CAS` 插入空桶 + `synchronized` 锁头节点 | 扩容时多线程协助迁移，用 `ForwardingNode` 标记 |
| `ConcurrentHashMap` 默认并发度 | `sizeCtl` 由容量与负载因子推导 | 不再有 JDK 7 的 `Segment` 分段锁 |
| `ThreadLocalMap` 初始容量 | 16 | 弱引用 `ThreadLocal` 为 key，泄漏根因见第 30 章 |

版本口径要答全：**JDK 8** 服务端默认 Parallel GC，**JDK 9 ~ JDK 20** 默认 G1；**JDK 21** 起分代 ZGC 成为 ZGC 的默认模式（JDK 21 中分代尚需显式开启，JDK 23 起直接默认），所以"JDK 21 默认 ZGC"这句话必须带版本前提才成立。

---

## A.8 分布式与 MQ 关键参数

### A.8.1 Kafka

| 参数 | 取值 | 语义与代价 |
|---|---|---|
| `acks` | `0` | 发出即认为成功，不等任何确认；可能丢 |
| `acks` | `1` | leader 写入本地日志即确认；leader 挂掉且未同步时会丢 |
| `acks` | `all`（等价 `-1`） | 等 ISR 中所有副本确认；配合 `min.insync.replicas` 才有意义 |
| `min.insync.replicas` | 常设 2（默认为 1） | 至少要写入成功的最小同步副本数；ISR 不足时生产者报 `NotEnoughReplicas` |
| `replication.factor` | 常设 3 | 副本总数；与上者的组合决定"还能坏几台" |
| `max.in.flight.requests.per.connection` | 默认 5 | 幂等生产者开启时必须 ≤ 5；不幂等时设 1 才严格有序 |
| `enable.idempotence` | 默认 false（3.0 起生产者默认 true） | 幂等 + 序列号去重，解决重试导致的重复 |
| `retries` | 默认 `2147483647` | Kafka 2.1 起生产者默认无限重试 |
| `ISR` | In-Sync Replicas | 与 leader 保持同步的副本集合；落后超过 `replica.lag.time.max.ms`（默认 30 s）被踢出 |
| `LEO` | Log End Offset | 每个副本下一条待写入消息的位点 |
| `HW` | High Watermark | ISR 中所有副本 LEO 的最小值；消费者只能读到 HW 之前 |
| `commitIndex` 的类比 | Kafka `HW` ≈ Raft `commitIndex` | 都表示"已确认可对外可见"的位点 |
| `unclean.leader.election.enable` | 默认 false（3.0 起） | 开启会选非 ISR 副本当 leader，牺牲一致性换可用性 |

### A.8.2 RocketMQ

| 项 | 取值 | 说明 |
|---|---|---|
| 延时级别数 | 18 级 | 固定级别：`1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h` |
| 默认 Topic 数 | 每个 Broker 自动创建 | `autoCreateTopicEnable` 生产应关闭 |
| 刷盘策略 | 同步刷盘 / 异步刷盘 | 同步刷盘保证不丢，但有性能代价 |
| 主从复制 | 同步 / 异步 | 同步双写才等价于 `acks=all` 的效果 |
| 消费模式 | 集群消费 / 广播消费 | 集群消费下同一 `consumerGroup` 分摊队列 |
| 死信队列 | `%DLQ%<ConsumerGroup>` | 重试次数耗尽后进入 |
| 重试次数 | 默认 16 次 | 按延时级别递减退避 |

### A.8.3 Raft、Snowflake 与一致性哈希

| 项 | 典型取值 / 结构 | 说明 |
|---|---|---|
| 选举超时（election timeout） | 随机 150~300 ms（etcd 默认） | 随机化避免选票瓜分 |
| 心跳间隔（heartbeat interval） | 通常为选举超时的 1/10~1/3，如 100 ms | 必须显著小于选举超时，否则跟随者频繁发起选举 |
| Raft 比例关系 | 心跳 ≪ 选举超时；超时随机区间要大于一次 RPC 往返 | 经验：选举超时 ≥ 10 × 心跳间隔 |
| 多数派 | `⌊N/2⌋ + 1` | 3 节点容忍 1 台故障，5 节点容忍 2 台 |
| Snowflake 位结构 | `1 + 41 + 10 + 12 = 64` 位 | 符号位 1（恒 0）+ 时间戳 41 + 机器 ID 10 + 序列号 12 |
| Snowflake 时间寿命 | 41 位毫秒 ≈ 69 年 | `2⁴¹ ms ÷ (365×24×3600×1000) ≈ 69.7 年` |
| Snowflake 单机 QPS | `2¹² = 4096/ms ≈ 409.6 万/s` | 每毫秒一个序列周期 |
| 时钟回拨处理 | 等待、抛异常、用备用位序列号 | 不能静默生成重复 ID |
| 一致性哈希扩容迁移比例 | 从 N 个节点加到 N+1，约 `1/(N+1)` 的键需要迁移 | 普通取模需迁移 `N/(N+1)`，几乎全量 |
| 虚拟节点 | 每物理节点 100~1000 个 | 缓解数据倾斜，节点数少时必备 |
| 槽位式方案 | Redis Cluster 16384 槽、Codis 1024 槽 | 迁移粒度固定，运维可控 |

```text
取模 vs 一致性哈希（N → N+1 个节点）：
  取模：      键映射几乎全部改变 ⟹ 需迁移约 N/(N+1) 的键（N=9 时约 90%）
  一致性哈希：只有落在新节点环区间的键需迁移 ⟹ 约 1/(N+1) 的键（N=9 时约 10%）

这就是"扩容抖动量级差一个数量级"的由来。
```

### A.8.4 分布式事务方案的不一致窗口

| 方案 | 一致性 | 不一致窗口 | 主要缺陷 |
|---|---|---|---|
| 2PC（XA） | 强（提交点后一致） | 协调者宕机期间参与者阻塞，窗口 = 协调者恢复时间 | 同步阻塞、单点、协调者故障导致悬挂 |
| 3PC | 弱化阻塞 | 预提交后宕机仍可能不一致 | 网络分区下依然不保证，工程上少用 |
| TCC | 最终一致 | Try 成功到 Confirm 之间 | 需写三个接口；空回滚、悬挂、幂等三坑 |
| Saga | 最终一致 | 每步到补偿之间 | 无隔离性，需语义锁或版本号防脏读 |
| 本地消息表 | 最终一致 | 本地事务提交到消息被消费之间 | 依赖消息表与业务库同库 |
| 最大努力通知 | 最终一致 | 通知重试期间 | 不保证到达，只保证尽力 |
| 事务消息（MQ 两阶段） | 最终一致 | 半消息到提交/回滚之间 | 需要 MQ 支持回查 |

> [!WARNING]
> "用了 TCC 就没有不一致了"是错的。TCC 只把不一致窗口压缩到 `Try → Confirm` 之间，这段窗口里资源已被预留但业务未完成，必须靠**幂等 + 防悬挂 + 空回滚**三件套兜住。

---

## A.9 端口、协议与默认值对照

### A.9.1 常用端口

| 端口 | 协议 | 服务 | 备注 |
|---|---|---|---|
| 20 / 21 | TCP | FTP 数据 / 控制 | 明文，已被 SFTP 替代 |
| 22 | TCP | SSH / SFTP | |
| 23 | TCP | Telnet | 明文，禁用 |
| 25 | TCP | SMTP | 提交端口常用 587 |
| 53 | TCP + UDP | DNS | 查询用 UDP，区域传送与大响应用 TCP |
| 67 / 68 | UDP | DHCP 服务端 / 客户端 | |
| 80 | TCP | HTTP | |
| 110 | TCP | POP3 | 加密 995 |
| 123 | UDP | NTP | |
| 143 | TCP | IMAP | 加密 993 |
| 161 / 162 | UDP | SNMP / Trap | |
| 443 | TCP | HTTPS | |
| 873 | TCP | rsync | |
| 3306 | TCP | MySQL | |
| 5432 | TCP | PostgreSQL | |
| 6379 | TCP | Redis | |
| 27017 | TCP | MongoDB | |
| 5672 | TCP | RabbitMQ（AMQP） | 管理界面 15672 |
| 9092 | TCP | Kafka | |
| 2181 | TCP | ZooKeeper | |
| 11211 | TCP + UDP | Memcached | |
| 8080 | TCP | HTTP 备用 / 应用容器 | 非标准，需显式指定 |

### A.9.2 UDP 与 TCP 的使用场景

| 维度 | TCP | UDP |
|---|---|---|
| 连接 | 面向连接，三次握手 | 无连接 |
| 可靠性 | 确认 + 重传 + 排序 | 不保证 |
| 头部开销 | 20 字节起 | 8 字节 |
| 传输形态 | 字节流（无消息边界） | 数据报（保留边界） |
| 拥塞控制 | 有 | 无（QUIC 在用户态实现） |
| 典型场景 | HTTP、数据库、文件传输 | DNS、DHCP、NTP、音视频、游戏、QUIC |

### A.9.3 HTTP 状态码速查（含语义陷阱）

| 状态码 | 语义 | 陷阱 / 注意 |
|---|---|---|
| 200 | 成功 | 不代表业务成功，需看响应体 |
| 301 | 永久重定向 | **浏览器会缓存**；做短链会损失统计与可控性 |
| 302 | 临时重定向 | 历史上部分客户端把 POST 改 GET |
| 303 | See Other | 明确要求改用 GET 访问新位置 |
| 307 | 临时重定向 | **保持原方法**（POST 仍为 POST） |
| 308 | 永久重定向 | 保持原方法，且会被缓存 |
| 304 | 未修改 | 靠 `ETag`/`Last-Modified` 命中协商缓存 |
| 400 | 请求语法错误 | 客户端问题，不该重试 |
| 401 | 未认证 | 缺凭据；应带 `WWW-Authenticate` |
| 403 | 已认证但无权限 | 与服务端 ACL 有关；与 401 的区分是高频考点 |
| 404 | 资源不存在 | 也可能被用于隐藏 403 |
| 408 | 请求超时 | 服务端等请求体超时 |
| 429 | 请求过多 | 应带 `Retry-After` |
| 499 | 客户端主动断开（Nginx 自定义） | 非标准码，用于统计客户端取消 |
| 500 | 服务端内部错误 | 业务代码抛异常 |
| 502 | 网关拿到无效响应 | 后端进程挂了/重启中；**上游连接失败** |
| 503 | 服务不可用 | 过载或维护，常配 `Retry-After` |
| 504 | 网关等待上游超时 | **上游连接成功但响应超时** |

```text
502 vs 504 的判据（面试常问"线上大量 5xx 你怎么查"）：
  502 Bad Gateway  ⟹ 网关连不上上游（进程没了 / 端口没监听 / 连接被拒）
  504 Gateway Timeout ⟹ 连上了，但上游在超时时间内没返回（慢查询 / 下游雪崩）

重试语义：
  502、504、503、429 通常可重试（配合退避与幂等）
  400、401、403、404、422 重试无意义，只会放大流量
```

`499` 是 Nginx 自定义状态码（客户端提前关闭连接），不是 HTTP 规范的一部分。面试里说"499 表示客户端取消"要带上"Nginx 定义"这个限定。

---

## A.10 一句话记忆锚点

- `归零不是删除`：`deleted` 标记位只是隐藏，purge 才真删。
- `%util 不是利用率`：队列非空即接近 100%，并发 I/O 下会失真。
- `RR 的幻读只在当前读出现`，快照读靠 ReadView 天然防幻读。
- `show index 看的是基数不是精确值`，Cardinality 是采样估算。
- `ABA 用版本号`，不是再比一次值。
- `Time Wait 属于主动关闭方`，服务端堆 TIME_WAIT 说明它先关的。
- `CLOSE_WAIT 堆积是代码没 close`，与内核参数无关。
- `MSS 在传输层、分片在网络层`，两者不可互换。
- `RTO 有最小值 200 ms`，别把 RTT 直接当重传超时。
- `快重传靠 3 个重复 ACK`，超时重传才是最后手段。
- `cwnd 初值 10 个 MSS` 是 RFC 6928 的现代口径。
- `窗口扩大因子最大 14`，上限是 `65535 × 2¹⁴`。
- `vm.swappiness = 0 不等于禁用 swap`。
- `overcommit = 1 会让 malloc 骗过你`，触碰时才 OOM。
- `dirty_ratio 触发同步回写`，`dirty_background_ratio` 是异步。
- `RAID5 随机写惩罚为 4`，RAID6 为 6，RAID10 为 2。
- `%i 用满也会 No space left`，`df -h` 看不出来。
- `du 与 df 不一致`：删了文件但进程还持有。
- `noatime 几乎无副作用`，是低风险高收益的挂载项。
- `innodb_redo_log_capacity 默认 100 MB`，8.0.30 起取代旧参数。
- `redo 是环形`，write pos 追上 checkpoint 就得停下来刷脏。
- `两阶段提交靠 XID 对齐`，binlog 有就补提交。
- `三层 B+ 树约 2000 万行`，根节点常驻内存。
- `key_len 可空加 1、varchar 加 2、utf8mb4 乘 4`。
- `RR 是 MySQL 默认`，PostgreSQL 默认是 RC。
- `innodb_lock_wait_timeout 默认 50 s`，超时不等于回滚。
- `hash 128 条 64 字节`是 listpack 转 hashtable 的界。
- `String 44 字节`是 embstr 与 raw 的分界。
- `maxmemory 默认 0`，不设淘汰策略等于没设。
- `16384 槽`让集群心跳位图只有 2 KB。
- `HyperLogLog 误差 0.81%`，固定 12 KB。
- `32 GB 是压缩指针拐点`，不是堆内存上限。
- `MaxTenuringThreshold 默认 15`，因为年龄位只有 4 位。
- `MetaspaceSize 是首次 GC 阈值`，不是初始大小。
- `HashMap 树化 8、退化 6、最小容量 64`。
- `Integer 缓存 -128~127`，超出即新对象。
- `acks=all 必须配 min.insync.replicas`，否则等于 acks=1。
- `HW 取 ISR 最小 LEO`，消费者只能读到 HW。
- `18 级延时消息`是 RocketMQ 的固定档位。
- `选举超时要大于 10 倍心跳`，否则选主抖动。
- `一致性哈希扩容迁移约 1/(N+1)`，取模是 N/(N+1)。
- `502 是连不上，504 是等超时`。
- `301 会被缓存`，短链要用 302 保留统计。
- `401 是没认证，403 是没权限`。

> [!TIP]
> 最后一小时的用法：按 A.1 → A.4 → A.5 → A.7 的顺序过一遍倍率与默认值，再把 A.10 的锚点逐条读出声。能在 10 分钟内复述出"倍率三档 + 三个版本坑（8.0.30 的 redo、JDK 21 的 ZGC、RFC 6928 的 cwnd）+ 两处反直觉（`%util`、`swappiness`）"，这一轮校准就到位了。

---

## 附：本附录的口径与版本约定

| 领域 | 版本口径 | 需要特别说明的可变项 |
|---|---|---|
| Linux | 5.x / 6.x 主线 | `somaxconn`、`tcp_max_syn_backlog`、`fs.file-max` 随版本与内存变化；`tcp_tw_recycle` 已于 4.12 移除 |
| MySQL | 8.0（部分标注 8.0.30 起） | redo 容量参数更名；行格式默认从 `COMPACT` 变 `DYNAMIC` |
| Redis | 7.x | `listpack` 已取代 `ziplist`；Set 编码参数为 7.2 起的新名字 |
| JVM | JDK 8 / 9+ / 21+ | 默认 GC 三次变更；分代 ZGC 的默认化有版本前提 |
| Kafka | 3.x | 幂等生产者与 `unclean.leader.election` 的默认值在 3.0 发生变化 |
| TCP 规范 | RFC 793 / 1122 / 5681 / 6298 / 6928 | `cwnd` 初值、RTO 计算、拥塞控制均以 RFC 为准，Linux 实现另有微调 |

## 延伸阅读

- RFC 793《Transmission Control Protocol》、RFC 6298《Computing TCP's Retransmission Timer》、RFC 6928《Increasing TCP's Initial Window》、RFC 5681《TCP Congestion Control》。
- Linux 手册页：`man 7 tcp`、`man 5 proc`、`man 8 sysctl`、`man 5 limits.conf`。
- MySQL 官方手册：InnoDB 参数、InnoDB 事务模型与锁、`innodb_redo_log_capacity` 变更说明。
- Redis 官方文档：内存优化（`memory-optimization`）、持久化、集群规格（`cluster-tutorial`）、键过期（`EXPIRE`）。
- Oracle 官方文档：`java` 命令参考（`-XX` 选项与默认 GC）、`HashMap` 与 `Integer` 的 API 说明。
- Apache Kafka 官方文档：生产者配置（`acks`、`min.insync.replicas`、`max.in.flight`）与复制设计。


---

> 作者: [破戒僧](https://onlyblacktea.cn)  
> URL: https://onlyblacktea.cn/book/backend/appendix-a/  

