预读在做什么
预读(read-ahead)是存储栈在检测到连续访问模式后,主动把后续数据块批量读入缓存的机制。它把多次随机寻道合并成一次大块读,顺序扫描类负载因此能显著提升吞吐。判读要点在于访问模式:顺序读有收益,随机读时预读的数据大多不会被用到,等于花钱买了不会被读的数据。
基本信息
| 生效层次 | 应用自身缓冲、文件系统、块层、控制器 / 存储池,多层各有各的预读 |
|---|---|
| Linux 块层口径 | blockdev --setra 以 512 字节扇区为单位;read_ahead_kb 以 KB 为单位 |
| 换算关系 | 4096 扇区 = 2 MiB;read_ahead_kb 写 2048 与 4096 扇区等价 |
| 生效方式 | 改后立即生效,无需重启,重启后可能被 udev 规则覆盖 |
| 典型收益负载 | 大文件顺序读、备份还原、日志与数据仓库扫描 |
| 典型受损负载 | 小随机读、OLTP 点查、多流交错访问 |
典型使用场景
- 大文件顺序读取吞吐上不去,先确认预读是否被设得过小
- 数据库服务器点查延迟偏高,排查是否因块层预读过大浪费了带宽
- 备份与还原窗口内临时放大预读以提高扫描速度,任务结束后回退
- 虚拟化宿主机上多虚拟机交错访问,评估预读是否引发读放大
- 排查存储读带宽被无意义占用:命中率低但读吞吐很高,通常就是预读在空转
查看与调整预读
查看与调整预读
# 查看当前预读扇区数与 KB 表示(两者指同一件事)
blockdev --getra /dev/sda
cat /sys/block/sda/queue/read_ahead_kb
# 调整为 4096 扇区,即 2 MiB
timeout 20 blockdev --setra 4096 /dev/sda
# 应用侧预读:顺序读大文件时放大缓冲
# 以厂商或工具文档为准,常见做法是加大块大小
# 用 fio 对比不同预读下的顺序读吞吐
fio --name=seq --rw=read --bs=1M --iodepth=8 --size=4G --filename=/data/big.bin各层预读逐层读法
| 层次 | 调整方式 | 适用判断 |
|---|---|---|
| 应用缓冲 | 调大读取块大小或缓冲尺寸 | 顺序扫描类程序最直接有效 |
| 文件系统 | 挂载或文件系统参数控制预读行为 | 与文件系统类型强相关,需查对应文档 |
| 块层 | read_ahead_kb 与 blockdev --setra | 通用且即时生效,是排查入口 |
| 控制器 / 存储池 | 阵列或池级预读策略 | 就近命中收益大,但多流场景易放大 |
预读不等于缓存命中:预读只是把数据取进缓存,是否命中取决于后续是否真的顺序读到。判断有没有收益,看命中率与顺序度两个指标,而不是只看吞吐。
多层预读叠加会造成读放大:应用、文件系统、块层、阵列同时开大预读,实际读入量可能是业务需求量的数倍,在多流并发下直接表现为带宽拥塞与延迟抖动。