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

使用说明

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

你要找什么去哪一节对应正文章节
一次内存访问、一次 RTT 差多少倍A.1第 1、3 章
swappiness、ulimit -n 该不该调A.2第 10、12 章
RAID 写惩罚怎么算A.3第 13 章
MSS 为什么是 1460A.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全书

注意

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


A.1 延迟数量级表(面试心算基准)

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

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

常用换算(面试官爱让现场算):

1
2
3
4
5
6
7
8
9
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

重要

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

提示

心算口诀:主存 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.swappiness60换出匿名页相对回收文件页的权重(0~100)数据库/缓存服务想优先保留匿名页,降到 1~10设 0 并非禁用 swap,内存紧张时仍可能换出;设太高会让热数据被换出,延迟抖动
vm.overcommit_memory0启发式过量提交:明显超额的分配才拒绝Redis fork 做 RDB 时被 fork 失败或 OOM 打断,可设 1设 1(总是允许)会让 malloc 成功但触碰时才 OOM,进程被 OOM killer 杀掉
net.core.somaxconn4096(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_retries6(约 127 s 后放弃)主动建连 SYN 重传次数缩短上游不可达时的建连等待调太小会在网络抖动时误判服务不可用
net.ipv4.tcp_synack_retries5被动方回 SYN+ACK 的重传次数与 tcp_max_syn_backlog 配合抗 flood调太小会让慢客户端连不上
net.ipv4.tcp_tw_reuse0(关闭)允许把 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_ratio20(百分比)脏页占内存达到该值,写进程同步回写并阻塞削峰:降低它让回写更早、单次阻塞更短调太低会让写吞吐下降;调太高会造成长时间卡顿式回写
vm.dirty_background_ratio10(百分比)达到该值,后台线程开始异步回写让回写更早启动,避免触顶调太低会频繁唤醒回写线程,与 dirty_ratio 靠太近会失去缓冲
vm.max_map_count65530单进程内存映射区数量上限Elasticsearch 等大量 mmap 的场景启动报错调太大会增加内核 vm_area_struct 内存开销
net.ipv4.ip_local_port_range32768 60999主动连接可用的本地临时端口范围客户端侧短连接 QPS 高、端口耗尽范围过大且 tcp_tw_reuse 关闭时会堆积更多 TIME_WAIT;扩大范围不影响其他进程已占用端口

警告

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

相关观测命令(口径要对):

1
2
3
4
5
6
7
8
9
# 看进程真正的软/硬限制,而不是当前 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 速查

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

A.3.2 容量与 inode 观测

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

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

1
2
3
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 0100%(N 块全用)无,坏一块全丢1——临时数据、缓存盘
RAID 150%允许坏 1 块2低系统盘
RAID 5(N−1)/N允许坏 1 块4(读旧数据 + 读旧校验 + 写新数据 + 写新校验)高(重建期再坏即全丢)读多写少的归档
RAID 6(N−2)/N允许坏 2 块6中大容量冷数据
RAID 1050%每镜像组可坏 1 块(视布局)2低(重建只拷一份)数据库、高并发随机写

写惩罚换算有效 IOPS:

1
2
3
4
5
6
7
8
9
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 让主控提前知道哪些块可回收
TRIMdiscard 挂载选项或定期 fstrim;不 TRIM 会让 SSD 误以为数据仍有效,WA 上升
磨损均衡主控把写分散到全部块上,寿命看 TBW(总写入字节数)而非通电时间
与数据库的关系随机写模式最伤 SSD;redo/binlog 的顺序写远比数据页随机写友好

小心

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


A.4 TCP/IP 参数与常量

A.4.1 报文头与 MTU/MSS

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

为什么必须做窗口扩大:带宽时延积(BDP)决定"在途数据量"上限。

1
2
3
4
5
6
7
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 sLinux 不直接用 RFC 建议的 2 min MSL,而是固定 60 s
RFC 793 建议 MSL2 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 stcp_syn_retries = 6 时首次建连约在 127 s 后放弃
平滑公式SRTT = (1 − α) × SRTT + α × RTT,α = 1/8指数加权移动平均
偏差估计`RTTVAR = (1 − β) × RTTVAR + β ×SRTT − RTT
重传超时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 = ssthreshNewReno 改进了部分确认的处理乘法减
超时重传后ssthresh = cwnd / 2,cwnd 回 1 MSS同(部分算法回 10 MSS 以内)回到慢启动

拥塞控制算法要点对照:

算法丢包判据增长规律核心思想已知短板
Tahoe超时或 3 个重复 ACK回到 cwnd = 1最早的完整实现恢复太慢
Reno3 个重复 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 默认 DYNAMICDYNAMIC 更依赖溢出页
主键聚簇索引,叶子节点存整行无主键时用唯一非空索引,再退化到隐藏 row_id

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

参数默认值含义丢数据窗口
innodb_buffer_pool_size128 MB缓存数据页与索引页(兼作自适应哈希)不涉及持久性;生产常设物理内存 50%~70%
innodb_lock_wait_timeout50 s行锁等待超时超时抛 Lock wait timeout exceeded,事务不一定回滚
innodb_flush_log_at_trx_commit1redo log 的刷盘策略1:每次提交都 fsync,几乎不丢;0:每秒刷一次,崩掉最多丢 1 s;2:写 OS 缓存,OS 崩溃丢、进程崩溃不丢
sync_binlog1binlog 刷盘策略(8.0 默认 1)与上者组合决定"提交但未落盘"的窗口;0 时由 OS 决定
innodb_log_buffer_size16 MBredo log 内存缓冲太大浪费,太小导致频繁刷
innodb_redo_log_capacity100 MB(8.0.30 起)redo 日志总容量上限替代 8.0.30 之前的 innodb_log_file_size(默认 48 MB)× innodb_log_files_in_group(默认 2)
innodb_io_capacity200后台刷脏的 IOPS 预算设太小会让刷脏跟不上,脏页堆积
innodb_flush_neighbors1(机械盘有利,SSD 建议 0)刷脏页时是否顺带刷相邻页与 SSD 随机写特性冲突
transaction_isolationREPEATABLE READ默认隔离级别注意 PostgreSQL 默认是 READ COMMITTED
max_connections151最大连接数连接是线程,过多会上下文切换
table_open_cache2000(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 环形结构与两阶段提交

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
redo log 环形结构(write pos 追着 checkpoint 跑)

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

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

两阶段提交时序(innodb_flush_log_at_trx_commit = 1、sync_binlog = 1):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
阶段一: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 页扣减)。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
单页可容纳的键数量:
  每对 (键, 指针) = 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 计算所需的字段宽度

类型 / 情形占用字节说明
TINYINT1有符号同宽
SMALLINT2
MEDIUMINT3
INT4
BIGINT8
FLOAT4
DOUBLE8
DATE3
TIME3不含小数秒
DATETIME5(旧版 8)MySQL 5.6.4 起 DATETIME 不再固定 8 字节
TIMESTAMP4
CHAR(n)n × 字符集字节数utf8mb4 下每字符 4 字节
VARCHAR(n)n × 字符集字节数 + 2变长长度前缀 2 字节
可空列额外 +1需要 1 字节标记 NULL
utf8mb4 单字符最多 4 字节utf8mb3/utf8 为 3 字节
联合索引只用前 k 列各列 key_len 相加用于判断到底用到了联合索引的几列
1
2
3
4
5
算例: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 都用到

提示

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


A.6 Redis 默认配置与阈值

A.6.1 编码转换阈值

参数默认值触发效果
hash-max-listpack-entries128Hash 字段数 ≤ 128 且值不大时用 listpack
hash-max-listpack-value64单个值超过 64 字节即转 hashtable(超阈值才转)
zset-max-listpack-entries128ZSet 元素数阈值
zset-max-listpack-value64ZSet 成员长度阈值
list-max-listpack-size128也可写负数表示字节数上限(如 -2 表示 8 KB)
set-max-intset-entries512全整数集合且元素数 ≤ 512 用 intset
set-max-listpack-entries1287.2 起 Set 用 listpack 而非 ziplist
hash-max-ziplist-entries1287.x 前的老名字,语义与 listpack 版一致
String 分界阈值编码
embstr 上限44 字节≤ 44 字节用 embstr(与 redisObject 一次分配)
raw> 44 字节单独分配 SDS
int可解析为 long 且不超范围直接存整数值,不存字符串
Redis 4.0 起sdshdr5/8/16/32/64按字符串长度选头结构,省内存
1
2
3
4
为什么是 44 字节:
  jemalloc 最小分配档位 64 字节
  redisObject = 16 字节(类型 + 编码 + LRU + 引用计数 + 指针)
  64 − 16 = 48,SDS 头需 3 字节 + 结尾 '\0' 1 字节 ⟹ 可用 44 字节

A.6.2 持久化

参数默认值含义风险
appendfsynceverysecAOF 刷盘策略always 最安全最慢;no 交给 OS,宕机可能丢数十秒
save3600 1 300 100 60 10000RDB 自动快照触发条件频率过高会频繁 fork,影响延迟
auto-aof-rewrite-percentage100AOF 增长到上次重写后的一倍就重写重写期间写放大
aof-use-rdb-preambleyes(4.0+)混合持久化:RDB 头 + AOF 增量兼容性依赖版本
rdbcompressionyesRDB 使用 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 槽位数16384CRC16(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 与堆

项版本口径默认值 / 语义
服务端默认 GCJDK 8Parallel Scavenge + Parallel Old(吞吐优先)
默认 GCJDK 9 ~ JDK 20G1
默认 GCJDK 21+分代 ZGC(开启分代模式后为默认低延迟收集器)
-Xmx 未设置所有版本物理内存的 1/4
-Xms 未设置所有版本物理内存的 1/64(早期实现口径)
SurvivorRatio默认 8Eden : 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 无此参数
1
2
3
4
5
6
7
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

参数取值语义与代价
acks0发出即认为成功,不等任何确认;可能丢
acks1leader 写入本地日志即确认;leader 挂掉且未同步时会丢
acksall(等价 -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默认 2147483647Kafka 2.1 起生产者默认无限重试
ISRIn-Sync Replicas与 leader 保持同步的副本集合;落后超过 replica.lag.time.max.ms(默认 30 s)被踢出
LEOLog End Offset每个副本下一条待写入消息的位点
HWHigh WatermarkISR 中所有副本 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⌋ + 13 节点容忍 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 单机 QPS2¹² = 4096/ms ≈ 409.6 万/s每毫秒一个序列周期
时钟回拨处理等待、抛异常、用备用位序列号不能静默生成重复 ID
一致性哈希扩容迁移比例从 N 个节点加到 N+1,约 1/(N+1) 的键需要迁移普通取模需迁移 N/(N+1),几乎全量
虚拟节点每物理节点 100~1000 个缓解数据倾斜,节点数少时必备
槽位式方案Redis Cluster 16384 槽、Codis 1024 槽迁移粒度固定,运维可控
1
2
3
4
5
取模 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 支持回查

警告

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


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

A.9.1 常用端口

端口协议服务备注
20 / 21TCPFTP 数据 / 控制明文,已被 SFTP 替代
22TCPSSH / SFTP
23TCPTelnet明文,禁用
25TCPSMTP提交端口常用 587
53TCP + UDPDNS查询用 UDP,区域传送与大响应用 TCP
67 / 68UDPDHCP 服务端 / 客户端
80TCPHTTP
110TCPPOP3加密 995
123UDPNTP
143TCPIMAP加密 993
161 / 162UDPSNMP / Trap
443TCPHTTPS
873TCPrsync
3306TCPMySQL
5432TCPPostgreSQL
6379TCPRedis
27017TCPMongoDB
5672TCPRabbitMQ(AMQP)管理界面 15672
9092TCPKafka
2181TCPZooKeeper
11211TCP + UDPMemcached
8080TCPHTTP 备用 / 应用容器非标准,需显式指定

A.9.2 UDP 与 TCP 的使用场景

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

A.9.3 HTTP 状态码速查(含语义陷阱)

状态码语义陷阱 / 注意
200成功不代表业务成功,需看响应体
301永久重定向浏览器会缓存;做短链会损失统计与可控性
302临时重定向历史上部分客户端把 POST 改 GET
303See 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网关等待上游超时上游连接成功但响应超时
1
2
3
4
5
6
7
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 是没权限。

提示

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


附:本附录的口径与版本约定

领域版本口径需要特别说明的可变项
Linux5.x / 6.x 主线somaxconn、tcp_max_syn_backlog、fs.file-max 随版本与内存变化;tcp_tw_recycle 已于 4.12 移除
MySQL8.0(部分标注 8.0.30 起)redo 容量参数更名;行格式默认从 COMPACT 变 DYNAMIC
Redis7.xlistpack 已取代 ziplist;Set 编码参数为 7.2 起的新名字
JVMJDK 8 / 9+ / 21+默认 GC 三次变更;分代 ZGC 的默认化有版本前提
Kafka3.x幂等生产者与 unclean.leader.election 的默认值在 3.0 发生变化
TCP 规范RFC 793 / 1122 / 5681 / 6298 / 6928cwnd 初值、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)与复制设计。