缓存还是一千万个键,值却变大了
缓存对象变大,变化的不只是容量。通过一个假设场景,分析 Lavik 如何将压力转移到响应字节量、替换写入和存储空间回收。
缓存对象增加了字段,键和请求速率却没有变化。应用仍在执行同样数量的查询,容量曲线却持续上升。Lavik 将键索引放在 DRAM、将值放在 SSD,这让此类场景值得单独评估:更大的值无需全部装入内存,但每次替换和响应仍然需要搬运更多字节。在基础设施评审中,对象增长应有独立的工作负载预算。
假设一个缓存以 String 值保存一千万个序列化对象。键、过期策略和访问分布保持不变,每个值则从 1 KiB 增至 8 KiB。按十进制 GB 计算,当前有效值的总字节数从 10.24 GB 增至 81.92 GB。这只是载荷计算,不包括键、记录元数据、旧版本和空闲空间预留,也不是 Lavik 内存或磁盘占用的实测结果。
索引分离了容量需求,但不意味着内存占用固定
Lavik 的运行时索引保留键身份、逻辑版本和记录位置,值则存储在文件、裸块设备或 SPDK NVMe 命名空间中。在这个场景里,载荷增长本身不会让顶层索引条目数量成倍增加。应用可以扩大对象,而不必仅为容纳完整值,就按同样幅度增加 DRAM。
这种分离并不保证进程内存恒定。活跃追加流使用暂存缓冲区,读取需要 I/O 缓冲区,请求和响应也有各自的生命周期。对于使用外部 extent 存储的大值,读取路径会读取、校验各个 extent,再组装逻辑值。这里的 8 KiB 假设并不能证明已经跨过 extent 使用阈值。Lavik 的内存限制约束持续保留的状态,并非严格的 RSS 上限,因此评估时应分别观察保留内存和 RSS。
QPS 不变,载荷流量可能增至八倍
继续假设该应用每秒替换 50,000 个完整对象。按十进制 MB 计算,写入携带的值字节量将从 51.2 MB/s 增至 409.6 MB/s。如果每秒成功读取 50,000 个完整对象,返回值的字节量也会发生相同变化。这些计算仅描述应用载荷,不包括协议封装,也不代表实测设备带宽、物理 I/O 次数或可达到的吞吐量。
对序列化对象来说,这一区别很实际:应用只修改一个字段,也可能需要替换整个 String。Lavik 追加不可变的记录版本,并把索引更新到新位置。普通替换发生后,在新版本刷写完成之前,旧的持久化版本仍计入物理有效数据。因此,键数量和当前有效对象数量都保持稳定时,存储层仍可能持续处理大量版本更替。
空间回收有明确的完成边界
过时数据占用的字节不会立即变成可分配空间。普通记录块只有在已持久化、不再活跃、没有 pin,且有效数据比例不超过 50% 时,才会成为 Lavik 的碎片整理候选。回收过程会搬迁当前记录,等待目标记录持久化并释放所有 pin,然后才能退役源块。之后,块还要经过分配位图和冷块复用流程。对象变大后,评估需要回答另一个问题:在实际覆盖写模式下,回收空间的速度能否跟上需求?
应同时观察可用存储块、脏暂存字节、待完成刷写和碎片整理活动。仅看当前有效载荷大小,无法判断持续替换还剩多少空间。也要区分写入确认与持久性:普通写入成功响应不代表同步 fsync 已完成。只检查成功响应的容量测试,既无法说明空间回收表现,也无法明确故障语义。
已发布基准没有测量这次对象扩容
2026 年 9 月 18 日的 beta SPDK 报告使用 1,024 字节的值、均匀随机 GET 或覆盖式 SET,以及深度为一的流水线。一千万键测试点持续 30 秒,十亿键测试点持续 60 秒。报告组合了 22 个新测得的 Lavik 数据点和 124 个复用的其他产品对照数据点,每种命令、连接数和服务端线程配置组合只测量一次。这些结果支持所记录的工作负载,不能用于预测 8 KiB 值的吞吐量,也不能证明另一种载荷大小下的持续回收能力。
同样,更大的值可能让值容量脱离 DRAM 更有吸引力,却不能据此认定整个部署节省了多少成本。值容量的算术关系不是总成本测量,也不代表延迟、持久性、可用性或运维服务等价。历史报告中的实例价格估算明确不是完整的总拥有成本比较。在这个场景下,成本模型除了计算保留更大载荷所需的容量,还应计入搬运这些载荷和回收空间所需的资源。
为载荷增长安排独立验收
在可丢弃的部署中,对比旧对象编码与拟采用的新编码,同时固定键数量和应用操作比例。为应用延迟、成功吞吐量、保留内存、RSS 和可用存储设定验收标准。加入持续覆盖写和有代表性的过期行为。既要观察流量持续时可用容量能否稳定,也要观察流量减小后后台维护能否跟上。这些是建议开展的评估步骤,不是新增的已报告测试。
Lavik 0.1.0-beta.1 是采用 Apache 2.0 许可证的 beta 项目,提供广泛的 Redis 兼容接口。采用前应验证应用依赖的具体命令和运维行为。可以从下面的基础配方开始:所提供的 Docker 回执记录了 ARM64 minimal beta 软件包在 Linux 7.0.12-linuxkit、glibc 2.39 环境下通过五项命令检查和一次正常关闭后的重启检查。这个有限结果不能验证假设中的大对象工作负载、SPDK 性能或断电恢复。部署仍需要 Linux 6.1 或更新版本、可用的 io_uring 和兼容的 glibc。
读写第一组键
先安装所选 Linux 软件包和 redis-cli(Ubuntu/Debian:sudo apt-get install redis-tools)。适用于 Minimal 和 Standard。在终端 1 进入解压后包含 ./lavik 的软件包目录,再粘贴下面的 Bash 脚本并保持运行。在终端 2 执行客户端命令。启动 Lavik 无需修改 PATH、使用 root 权限或配置 systemd 服务。
set -euo pipefail
if [ ! -x ./lavik ]; then
echo "Run this from the extracted Lavik package directory (the folder containing ./lavik)." >&2
exit 1
fi
mkdir -p /tmp/lavik-example
if [ ! -e /tmp/lavik-example/data ]; then
fallocate -l 512M /tmp/lavik-example/data
fi
./lavik --bind 127.0.0.1 --port 6379 --metrics-port 0 \
--network kernel --storage uring \
--threads 2 --no-pin-workers --registered-buffer-mb-per-worker 64 \
--shutdown-checkpoint --data-file /tmp/lavik-example/data \
--log-dir /tmp/lavik-example/logs$ redis-cli -h 127.0.0.1 -p 6379 --raw PING
PONG
$ redis-cli -h 127.0.0.1 -p 6379 --raw SET greeting 'hello from Lavik'
OK
$ redis-cli -h 127.0.0.1 -p 6379 --raw GET greeting
hello from Lavik
$ redis-cli -h 127.0.0.1 -p 6379 --raw HSET user:42 name Ada
1
$ redis-cli -h 127.0.0.1 -p 6379 --raw HGET user:42 name
Ada✓ 查看实际执行记录
0.1.0-beta.1 · Linux-7.0.12-linuxkit-aarch64-with-glibc2.39 · 2026-10-05
此记录验证功能行为,不验证 NVMe 性能、断电持久性或 SLA。
{
"recipeId": "basic-commands",
"platform": "Linux-7.0.12-linuxkit-aarch64-with-glibc2.39",
"steps": [
{
"argv": [
"redis-cli",
"-h",
"127.0.0.1",
"-p",
"6379",
"--raw",
"PING"
],
"expected": "PONG",
"actual": "PONG"
},
{
"argv": [
"redis-cli",
"-h",
"127.0.0.1",
"-p",
"6379",
"--raw",
"SET",
"greeting",
"hello from Lavik"
],
"expected": "OK",
"actual": "OK"
},
{
"argv": [
"redis-cli",
"-h",
"127.0.0.1",
"-p",
"6379",
"--raw",
"GET",
"greeting"
],
"expected": "hello from Lavik",
"actual": "hello from Lavik"
},
{
"argv": [
"redis-cli",
"-h",
"127.0.0.1",
"-p",
"6379",
"--raw",
"HSET",
"user:42",
"name",
"Ada"
],
"expected": "1",
"actual": "1"
},
{
"argv": [
"redis-cli",
"-h",
"127.0.0.1",
"-p",
"6379",
"--raw",
"HGET",
"user:42",
"name"
],
"expected": "Ada",
"actual": "Ada"
}
],
"status": "passed",
"binaryVersion": "lavik 0.1.0-beta.1",
"executableOnPath": false,
"workingDirectory": "/opt/lavik",
"sourceCommit": "3955b98d43b312324aa8d52775df52cfb111c0d0",
"packageVersion": "v0.1.0-beta.1",
"gracefulRestart": "passed"
}