把磁盘放在推理分层的第二层,容量只回答“内容能不能放下”,读写次数回答“每个请求要为它多等多久”。用便宜介质承接不常访问的内容,收益来自热层挡住大部分请求;一旦热层开始频繁把请求打到第二层,磁盘的寻道和随机读延迟就会叠加到首字延迟上。先做一件事:把每个统计窗口里热层命中多少次、冷层被读取多少次、这些读取摊在多少个请求上记成数字,再看首字延迟跟着哪个数字波动。容量扩不扩,等次数和延迟的对照关系出来再定。
在 Colibrì 这类分层推理里,磁盘只当第二层,容量通常够用就行,先别把精力放在扩容上。固定统计窗口和请求样本,把热层命中与冷层回读分开计数,再和首字延迟并排看:次数涨而延迟平稳就先继续观察,次数涨且首字延迟同步抬高,再把冷层换到更快的介质、用同一批请求复测。每次只改一个变量,换介质和调参数不要同时做,否则对照结果没有意义。
把每层被读取的次数记成一张表
要让读写次数可比,先固定采集口径:统计窗口用“连续 N 个请求”或“固定时间片”二选一,之后所有对照都沿用同一种;请求数量必须一起记,它是分母,没有分母的次数放在不同批次之间比没有意义。并发、上下文长度、批次标记也一并写进记录,否则两次采样差异说不清是参数变了还是负载变了。
记录表建议至少包含下面这些字段,每行一次采样:
| 字段 | 含义 | 采集方式 |
|---|---|---|
| window_id | 统计窗口编号 | 手动或脚本生成,连续递增 |
| req_count | 窗口内请求数(分母) | 入口日志计数 |
| hot_hit | 热层命中次数 | 热层读取日志或计数器 |
| cold_read | 冷层读取次数 | 冷层目录的读取事件或设备统计 |
| cold_write | 冷层写入次数(首次落盘) | 同上,与读取分开记 |
| read_per_req | 每请求冷层回读次数 | cold_read / req_count |
| ttft_p50 / ttft_p95 | 首字延迟分位 | 请求侧计时日志 |
| media / batch | 冷层介质类型、批次标记 | 挂载信息与人工标注 |
冷层回读和正常加载要分开。正常加载指某份内容第一次进入第二层后被读上来,属于一次性成本,通常在窗口内只出现一次;回读指同一份内容在同一窗口里被反复读出,往往发生在热层淘汰之后或缓存未命中同一标识时。判断方法有两个:一是按内容标识统计窗口内出现次数,大于 1 的计入回读;二是看事件顺序,写入事件之后紧跟着的第一次读取算加载,之后的重复读取算回读。日志里没有内容标识时,退一步用文件路径或分片名做近似键。
对照读写次数与首字延迟找相关性
把两张数据并排放,最省事的做法是同一张表里左半部分是次数、右半部分是延迟,行按 window_id 对齐,一行一个窗口。这样扫一眼就能看出 read_per_req 抬高的窗口里 ttft_p95 是否同向抬高,还是两条线各自摆动。
- 次数涨、首字延迟也涨,且延迟抬高的量级和回读次数成比例:瓶颈在回读路径,进入下一节的介质对照。
- 次数涨、首字延迟平稳:说明介质带宽和读取路径还有余量,回读被流水线摊掉了。这种时候不建议立刻调参,先把窗口拉长再采几批,确认不是样本太少的假象。
- 次数平稳、首字延迟抖动:问题多半不在分层,去查热层是否有大块换入换出、并发是否突增、单请求上下文是否变长。
判断时注意样本量:一个窗口只有十几个请求时,单次慢请求就能把 p95 拉上去,这种数字不要当成结论。建议连续采若干窗口、覆盖至少一次负载高峰再下判断。
把冷层换到更快的介质再测同一批请求
这一步回答的是“慢在次数还是慢在介质”。如果同一批请求、同样窗口下回读次数没有变化,只把冷层换到更快的盘之后首字延迟明显下来了,那是介质速度问题;如果换了盘延迟几乎不动,说明成本主要是访问次数本身,要回头去降次数。
操作顺序可以按下面走,字段名按你环境里的实际键名替换:
- 把当前配置里的冷层目录指向新介质的挂载点,改完先做配置回显或语法检查再重启。
- 确认目录权限和属主与运行用户一致,挂载点是本地盘而不是网络盘。
- 启动后验证是否生效:查看运行日志里打印的实际冷层路径,用
mount | grep <冷层挂载点>确认设备,再发一个请求,看新目录下是否出现对应文件。 - 从系统侧确认读确实落在新设备上:
iostat -x 1观察对应设备在请求期间的读速率与等待时间。
# 冷层目录配置示例,键名以实际版本为准
storage:
hot_tier:
path: /dev/shm/colibri_hot
max_bytes: 0 # 热层上限,按内存余量设定
cold_tier:
path: /mnt/nvme0/colibri_cold # 换介质只改这一行
read_only: true
verify_on_start: true
对照结果按同输入记录:同一批请求、同样并发、同样窗口长度、同样上下文长度,只变介质这一项,其余字段照抄上一节的表。两批结果放同一张表里对比,不要跨天比。
读写次数降不下来时该动哪里
次数压不下来,通常是热层装不下工作集,或者同一批内容被拆得太碎反复进出。三个可调项各自的方向不同,建议一次只动一个:
- 热层上限:调大能减少淘汰,回读次数通常下降,代价是常驻内存变高,可能挤压推理本身。验证时看 hot_hit 与 read_per_req 是否同向变化,同时盯进程内存峰值。
- 请求批量:把小请求合并或让相邻请求共享同一段内容,回读次数会被摊薄,但单请求的首字延迟可能变大,内存占用峰值也会抬高。验证时同时看 read_per_req 和 ttft_p50,不能只看其中一个。
- 上下文长度:缩短输入能直接减少需要加载的分片数量,回读次数下降最直接。验证时按同一批请求跑长短两个版本,比较 cold_read 的绝对值而不是比值。
每次改动保留改动前后的 window_id 和批次标记,用同一张记录表累计。若三项都动过、次数仍然随请求量线性上涨,说明工作集本身就超过热层容量,这时候再考虑扩容或换介质才有依据;在没有次数数据支撑前扩容,往往只是把问题往后推。